@fy-stack/cli 0.0.147-3-preview → 0.0.147-alpha.301
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-infra.js.d.ts","sourceRoot":"","sources":["../../../src/commands/assets/get-infra.js.ts"],"names":[],"mappings":"AAEA,KAAK,kBAAkB,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,kBAAkB;;;
|
|
1
|
+
{"version":3,"file":"get-infra.js.d.ts","sourceRoot":"","sources":["../../../src/commands/assets/get-infra.js.ts"],"names":[],"mappings":"AAEA,KAAK,kBAAkB,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,kBAAkB;;;EAkElD"}
|
|
@@ -5,7 +5,8 @@ const get_cdk_json_1 = require("./get-cdk-json");
|
|
|
5
5
|
function getInfra(params) {
|
|
6
6
|
const infraFile = `#!/usr/bin/env node
|
|
7
7
|
const cdk = require("aws-cdk-lib");
|
|
8
|
-
const { FullStackConstruct
|
|
8
|
+
const { FullStackConstruct } = require("@fy-stack/fullstack-construct");
|
|
9
|
+
const { AppType } = require("@fy-stack/types");
|
|
9
10
|
const { STSClient, GetCallerIdentityCommand } = require('@aws-sdk/client-sts');
|
|
10
11
|
|
|
11
12
|
const env = {
|
|
@@ -31,7 +32,12 @@ class AppStack extends cdk.Stack {
|
|
|
31
32
|
name: \${appName},
|
|
32
33
|
ownerArn: props.ownerArn,
|
|
33
34
|
storage: { retainOnDelete: false },
|
|
34
|
-
|
|
35
|
+
lambda: {
|
|
36
|
+
demo: {
|
|
37
|
+
type: AppType.NODE_APP,
|
|
38
|
+
output: './dist'
|
|
39
|
+
}
|
|
40
|
+
}
|
|
35
41
|
});
|
|
36
42
|
}
|
|
37
43
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init-app.d.ts","sourceRoot":"","sources":["../../src/commands/init-app.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init-app.d.ts","sourceRoot":"","sources":["../../src/commands/init-app.ts"],"names":[],"mappings":"AAqCA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAoBvC,wBAAsB,OAAO,CAAC,KAAK,EAAE,YAAY,iBAqThD"}
|
|
@@ -4,40 +4,42 @@ exports.initApp = initApp;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const fs = tslib_1.__importStar(require("node:fs"));
|
|
6
6
|
const path = tslib_1.__importStar(require("node:path"));
|
|
7
|
+
const client_cloudfront_1 = require("@aws-sdk/client-cloudfront");
|
|
7
8
|
const client_iam_1 = require("@aws-sdk/client-iam");
|
|
9
|
+
const client_ssm_1 = require("@aws-sdk/client-ssm");
|
|
8
10
|
const client_sts_1 = require("@aws-sdk/client-sts");
|
|
9
11
|
const get_infra_js_1 = require("./assets/get-infra.js");
|
|
10
12
|
const get_yaml_file_1 = require("./assets/get-yaml-file");
|
|
11
13
|
const iamClient = new client_iam_1.IAMClient();
|
|
14
|
+
const ssmClient = new client_ssm_1.SSMClient();
|
|
15
|
+
const cdnClient = new client_cloudfront_1.CloudFrontClient();
|
|
12
16
|
async function attachManagedPolicies(roleName, attachedPolices) {
|
|
13
17
|
for (const policy of attachedPolices ?? []) {
|
|
14
18
|
await iamClient.send(new client_iam_1.AttachRolePolicyCommand({
|
|
15
19
|
PolicyArn: policy.PolicyArn,
|
|
16
|
-
RoleName: roleName
|
|
20
|
+
RoleName: roleName,
|
|
17
21
|
}));
|
|
18
22
|
}
|
|
19
23
|
}
|
|
20
24
|
async function initApp(props) {
|
|
21
25
|
const stsClient = new client_sts_1.STSClient();
|
|
22
26
|
const user = await stsClient.send(new client_sts_1.GetCallerIdentityCommand());
|
|
23
|
-
const username = user.Arn?.split(
|
|
27
|
+
const username = user.Arn?.split('/').pop();
|
|
24
28
|
if (!username)
|
|
25
|
-
throw new Error(
|
|
26
|
-
const isRoot = username.endsWith(
|
|
29
|
+
throw new Error('username not found');
|
|
30
|
+
const isRoot = username.endsWith(':root');
|
|
27
31
|
const workingDir = process.cwd();
|
|
28
32
|
if (props.githubRepo) {
|
|
29
33
|
const githubFolderPath = path.join(workingDir, '.github/workflows/');
|
|
30
|
-
if (!fs.existsSync(githubFolderPath))
|
|
34
|
+
if (!fs.existsSync(githubFolderPath)) {
|
|
31
35
|
fs.mkdirSync(githubFolderPath, { recursive: true });
|
|
32
|
-
|
|
33
|
-
if (fs.existsSync(deployYamlPath))
|
|
34
|
-
throw new Error("fy-stack.deploy.yml already exists");
|
|
36
|
+
}
|
|
35
37
|
const existingRes = await iamClient.send(new client_iam_1.ListOpenIDConnectProvidersCommand());
|
|
36
38
|
let idProviderArn;
|
|
37
39
|
const providerUrl = 'token.actions.githubusercontent.com';
|
|
38
40
|
for (const i in existingRes.OpenIDConnectProviderList ?? []) {
|
|
39
41
|
const provider = await iamClient.send(new client_iam_1.GetOpenIDConnectProviderCommand({
|
|
40
|
-
OpenIDConnectProviderArn: existingRes.OpenIDConnectProviderList?.[i].Arn
|
|
42
|
+
OpenIDConnectProviderArn: existingRes.OpenIDConnectProviderList?.[i].Arn,
|
|
41
43
|
}));
|
|
42
44
|
if (provider.Url === providerUrl) {
|
|
43
45
|
idProviderArn = existingRes.OpenIDConnectProviderList?.[i].Arn;
|
|
@@ -47,109 +49,192 @@ async function initApp(props) {
|
|
|
47
49
|
if (!idProviderArn) {
|
|
48
50
|
const idProviderRes = await iamClient.send(new client_iam_1.CreateOpenIDConnectProviderCommand({
|
|
49
51
|
Url: providerUrl,
|
|
50
|
-
ClientIDList: ['sts.amazonaws.com']
|
|
52
|
+
ClientIDList: ['sts.amazonaws.com'],
|
|
51
53
|
}));
|
|
52
|
-
if (!idProviderRes.OpenIDConnectProviderArn)
|
|
53
|
-
throw new Error('unable to Github open id provider');
|
|
54
54
|
idProviderArn = idProviderRes.OpenIDConnectProviderArn;
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
56
|
+
if (!idProviderArn) {
|
|
57
|
+
throw new Error('unable to create Github OpenID provider');
|
|
58
|
+
}
|
|
59
|
+
const roleName = `${props.app}GithubRole`;
|
|
60
|
+
let roleArn;
|
|
61
|
+
try {
|
|
62
|
+
const existingRole = await iamClient.send(new client_iam_1.GetRoleCommand({ RoleName: roleName }));
|
|
63
|
+
roleArn = existingRole.Role?.Arn;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
/** empty */
|
|
67
|
+
}
|
|
68
|
+
if (!roleArn) {
|
|
69
|
+
const roleRes = await iamClient.send(new client_iam_1.CreateRoleCommand({
|
|
70
|
+
RoleName: roleName,
|
|
71
|
+
AssumeRolePolicyDocument: JSON.stringify({
|
|
72
|
+
Version: '2012-10-17',
|
|
73
|
+
Statement: [
|
|
74
|
+
{
|
|
75
|
+
Effect: 'Allow',
|
|
76
|
+
Principal: { Federated: idProviderArn },
|
|
77
|
+
Action: 'sts:AssumeRoleWithWebIdentity',
|
|
78
|
+
Condition: {
|
|
79
|
+
StringEquals: {
|
|
80
|
+
'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com',
|
|
81
|
+
},
|
|
82
|
+
StringLike: {
|
|
83
|
+
'token.actions.githubusercontent.com:sub': `repo:${props.githubRepo}:*`,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
}),
|
|
89
|
+
}));
|
|
90
|
+
roleArn = roleRes.Role?.Arn;
|
|
91
|
+
}
|
|
92
|
+
if (!roleArn) {
|
|
72
93
|
throw new Error('unable to Github role');
|
|
73
|
-
|
|
94
|
+
}
|
|
74
95
|
if (isRoot) {
|
|
75
96
|
await iamClient.send(new client_iam_1.AttachRolePolicyCommand({
|
|
76
97
|
PolicyArn: 'arn:aws:iam::aws:policy/AdministratorAccess',
|
|
77
|
-
RoleName:
|
|
98
|
+
RoleName: roleName,
|
|
78
99
|
}));
|
|
79
100
|
}
|
|
80
101
|
else {
|
|
102
|
+
// clone policies from logged in aws user to openid user
|
|
81
103
|
// attach managed polices
|
|
82
104
|
const attachedPolices = await iamClient.send(new client_iam_1.ListAttachedUserPoliciesCommand({
|
|
83
|
-
UserName: username
|
|
105
|
+
UserName: username,
|
|
84
106
|
}));
|
|
85
107
|
await attachManagedPolicies(roleName, attachedPolices.AttachedPolicies);
|
|
86
108
|
// attach inline polices
|
|
87
109
|
const inlinePolices = await iamClient.send(new client_iam_1.ListUserPoliciesCommand({
|
|
88
|
-
UserName: username
|
|
110
|
+
UserName: username,
|
|
89
111
|
}));
|
|
90
112
|
for (const policyName of inlinePolices.PolicyNames ?? []) {
|
|
91
|
-
const policyDocument = await iamClient.send(new client_iam_1.GetUserPolicyCommand({
|
|
113
|
+
const policyDocument = await iamClient.send(new client_iam_1.GetUserPolicyCommand({
|
|
114
|
+
UserName: username,
|
|
115
|
+
PolicyName: policyName,
|
|
116
|
+
}));
|
|
92
117
|
if (!policyDocument.PolicyDocument)
|
|
93
118
|
continue;
|
|
94
119
|
await iamClient.send(new client_iam_1.PutRolePolicyCommand({
|
|
95
120
|
PolicyName: policyName,
|
|
96
121
|
PolicyDocument: decodeURIComponent(policyDocument.PolicyDocument),
|
|
97
|
-
RoleName:
|
|
122
|
+
RoleName: roleName,
|
|
98
123
|
}));
|
|
99
124
|
}
|
|
100
125
|
const groups = await iamClient.send(new client_iam_1.ListGroupsForUserCommand({
|
|
101
|
-
UserName: username
|
|
126
|
+
UserName: username,
|
|
102
127
|
}));
|
|
103
128
|
for (const group of groups.Groups ?? []) {
|
|
104
129
|
// attach managed polices
|
|
105
130
|
const attachedPolices = await iamClient.send(new client_iam_1.ListAttachedGroupPoliciesCommand({
|
|
106
|
-
GroupName: group.GroupName
|
|
131
|
+
GroupName: group.GroupName,
|
|
107
132
|
}));
|
|
108
133
|
await attachManagedPolicies(roleName, attachedPolices.AttachedPolicies);
|
|
109
134
|
// attach inline polices
|
|
110
135
|
const inlinePolices = await iamClient.send(new client_iam_1.ListGroupPoliciesCommand({
|
|
111
|
-
GroupName: group.GroupName
|
|
136
|
+
GroupName: group.GroupName,
|
|
112
137
|
}));
|
|
113
138
|
for (const policyName of inlinePolices.PolicyNames ?? []) {
|
|
114
|
-
const policyDocument = await iamClient.send(new client_iam_1.GetUserPolicyCommand({
|
|
139
|
+
const policyDocument = await iamClient.send(new client_iam_1.GetUserPolicyCommand({
|
|
140
|
+
UserName: username,
|
|
141
|
+
PolicyName: policyName,
|
|
142
|
+
}));
|
|
115
143
|
if (!policyDocument.PolicyDocument)
|
|
116
144
|
continue;
|
|
117
145
|
await iamClient.send(new client_iam_1.PutRolePolicyCommand({
|
|
118
146
|
PolicyName: policyName,
|
|
119
147
|
PolicyDocument: decodeURIComponent(policyDocument.PolicyDocument),
|
|
120
|
-
RoleName:
|
|
148
|
+
RoleName: roleName,
|
|
121
149
|
}));
|
|
122
150
|
}
|
|
123
151
|
}
|
|
124
152
|
}
|
|
125
|
-
const yamlFile = (0, get_yaml_file_1.getYAMLFile)({
|
|
126
|
-
|
|
153
|
+
const yamlFile = (0, get_yaml_file_1.getYAMLFile)({
|
|
154
|
+
...props,
|
|
155
|
+
roleArn,
|
|
156
|
+
region: await stsClient.config.region(),
|
|
157
|
+
});
|
|
158
|
+
const deployYamlPath = path.join(githubFolderPath, 'fy-stack.deploy.yml');
|
|
159
|
+
if (!fs.existsSync(deployYamlPath)) {
|
|
160
|
+
fs.writeFileSync(deployYamlPath, yamlFile);
|
|
161
|
+
}
|
|
127
162
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
163
|
+
/* Configure image CDN cache policy */
|
|
164
|
+
const imagePolicyParameterName = '/fy-stack/ImagePolicyID';
|
|
165
|
+
const imageCachePolicyConfig = {
|
|
166
|
+
Name: 'AppImagePolicy',
|
|
167
|
+
MinTTL: 0,
|
|
168
|
+
DefaultTTL: 31536000,
|
|
169
|
+
MaxTTL: 31536000,
|
|
170
|
+
ParametersInCacheKeyAndForwardedToOrigin: {
|
|
171
|
+
EnableAcceptEncodingBrotli: true,
|
|
172
|
+
EnableAcceptEncodingGzip: true,
|
|
173
|
+
QueryStringsConfig: { QueryStringBehavior: 'all' },
|
|
174
|
+
HeadersConfig: { HeaderBehavior: 'none' },
|
|
175
|
+
CookiesConfig: { CookieBehavior: 'none' },
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
let imagePolicy;
|
|
179
|
+
try {
|
|
180
|
+
const foundParam = await ssmClient.send(new client_ssm_1.GetParameterCommand({ Name: imagePolicyParameterName }));
|
|
181
|
+
if (foundParam.Parameter?.Value) {
|
|
182
|
+
const existingCachePolicy = await cdnClient.send(new client_cloudfront_1.GetCachePolicyCommand({
|
|
183
|
+
Id: foundParam.Parameter.Value,
|
|
184
|
+
}));
|
|
185
|
+
await cdnClient.send(new client_cloudfront_1.UpdateCachePolicyCommand({
|
|
186
|
+
Id: foundParam.Parameter?.Value,
|
|
187
|
+
CachePolicyConfig: imageCachePolicyConfig,
|
|
188
|
+
IfMatch: existingCachePolicy.ETag,
|
|
189
|
+
}));
|
|
190
|
+
imagePolicy = existingCachePolicy.CachePolicy;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
/* empty */
|
|
195
|
+
}
|
|
196
|
+
if (!imagePolicy) {
|
|
197
|
+
console.log('image cache policy not found, attempting to create...');
|
|
198
|
+
const cachePolicy = await cdnClient.send(new client_cloudfront_1.CreateCachePolicyCommand({
|
|
199
|
+
CachePolicyConfig: imageCachePolicyConfig,
|
|
200
|
+
}));
|
|
201
|
+
imagePolicy = cachePolicy.CachePolicy;
|
|
202
|
+
}
|
|
203
|
+
if (!imagePolicy) {
|
|
204
|
+
throw new Error('unable to get image cache policy');
|
|
205
|
+
}
|
|
206
|
+
await ssmClient.send(new client_ssm_1.PutParameterCommand({
|
|
207
|
+
Value: imagePolicy.Id,
|
|
208
|
+
Name: imagePolicyParameterName,
|
|
209
|
+
Type: 'String',
|
|
210
|
+
Overwrite: true,
|
|
211
|
+
}));
|
|
134
212
|
const packageJsonPath = path.join(workingDir, 'package.json');
|
|
135
213
|
const infraPath = path.join(workingDir, 'infra.js');
|
|
136
214
|
const cdkPath = path.join(workingDir, 'cdk.json');
|
|
137
|
-
if (!fs.existsSync(packageJsonPath))
|
|
215
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
138
216
|
throw new Error('unable to find package.json file');
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
217
|
+
}
|
|
218
|
+
const packageJsonFile = JSON.parse(fs.readFileSync(path.join(workingDir, 'package.json'), 'utf8'));
|
|
219
|
+
packageJsonFile['devDependencies'] = {
|
|
220
|
+
...packageJsonFile['devDependencies'],
|
|
221
|
+
'aws-cdk': packageJsonFile.devDependencies?.['aws-cdk'] ?? '^2.1016.1',
|
|
222
|
+
'aws-cdk-lib': packageJsonFile.devDependencies?.['aws-cdk-lib'] ?? '^2.229.1',
|
|
223
|
+
'@aws-sdk/client-sts': packageJsonFile.devDependencies?.['@aws-sdk/client-sts'] ?? '^3.799.0',
|
|
224
|
+
constructs: packageJsonFile.devDependencies?.['constructs'] ?? '^10.4.3',
|
|
225
|
+
'@fy-stack/fullstack-construct': packageJsonFile.devDependencies?.['@fy-stack/fullstack-construct'] ??
|
|
226
|
+
'^0.0.147',
|
|
227
|
+
'@fy-stack/types': packageJsonFile.devDependencies?.['@fy-stack/types'] ?? '^0.0.147',
|
|
149
228
|
};
|
|
229
|
+
const domain = props.domainName
|
|
230
|
+
? `process.env.ENVIRONMENT === "production" ? "${props.domainName}" : \`\${process.env.ENVIRONMENT}.${props.domainName}\``
|
|
231
|
+
: undefined;
|
|
232
|
+
const { infraFile, cdkJsonFile } = (0, get_infra_js_1.getInfra)({ app: props.app, domain });
|
|
233
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJsonFile, null, 2));
|
|
150
234
|
if (!fs.existsSync(cdkPath))
|
|
151
235
|
fs.writeFileSync(cdkPath, cdkJsonFile);
|
|
152
|
-
fs.
|
|
153
|
-
|
|
154
|
-
|
|
236
|
+
if (!fs.existsSync(infraPath)) {
|
|
237
|
+
fs.writeFileSync(infraPath, infraFile);
|
|
238
|
+
}
|
|
239
|
+
console.log('App initialized, run npm install to complete');
|
|
155
240
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fy-stack/cli",
|
|
3
|
-
"version": "0.0.147-
|
|
3
|
+
"version": "0.0.147-alpha.301",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@aws-sdk/client-iam": "^3.731.1",
|
|
6
6
|
"@aws-sdk/client-sts": "^3.731.1",
|
|
7
|
+
"@aws-sdk/client-ssm": "^3.731.1",
|
|
8
|
+
"@aws-sdk/client-cloudfront": "^3.731.1",
|
|
7
9
|
"@inquirer/prompts": "^7.2.3",
|
|
8
10
|
"commander": "^13.0.0",
|
|
9
11
|
"tslib": "^2.3.0"
|