@raisenow/tamaro-cli 1.0.25 → 1.1.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.
@@ -1,36 +1,31 @@
1
1
  import {existsSync} from 'fs'
2
2
  import {
3
- assertProfileValid,
4
- AwsOptions,
3
+ assertProfilesPresent,
4
+ prepareFlags,
5
+ promptBucket,
6
+ promptProfile,
5
7
  withAwsAuthenticate,
6
- prepareFlags as prepareAwsFlags,
7
8
  } from 'lib/aws'
9
+ import {halt, runCommandSync} from 'lib/command'
8
10
  import {logCommand, logDataTable, logTitle} from 'lib/logging'
9
- import {getIfCoreFns, getPaths, getWidgetUuid} from 'lib/resolve'
10
11
  import {notify} from 'lib/notifier'
11
- import {halt, runCommandSync} from 'lib/command'
12
-
13
- ///////////////////////////////////////////////////////////////////////////////
14
-
15
- export type DeployEmailConfigOptions = AwsOptions & {
16
- bucket: string
17
- }
12
+ import {getIfCoreFns, getPaths, getWidgetUuid} from 'lib/resolve'
18
13
 
19
14
  ///////////////////////////////////////////////////////////////////////////////
20
15
 
21
- export const deployEmailConfig = async (
22
- options: DeployEmailConfigOptions,
23
- ): Promise<void> => {
16
+ export const deployEmailConfig = async (): Promise<void> => {
24
17
  const {ifCore} = getIfCoreFns()
25
18
  const paths = getPaths(ifCore)
26
19
  const emailConfigPath = `${paths.app}/email-config`
27
20
 
28
- assertOptionsValid(options)
29
21
  assertIsNotCore()
30
22
  assertEmailConfigPathExists(emailConfigPath)
23
+ assertProfilesPresent()
24
+
25
+ const profile = await promptProfile()
26
+ const bucket = await promptBucket()
31
27
 
32
- const flags = prepareFlags([], options).join(' ')
33
- const {bucket} = options
28
+ const flags = prepareFlags([], {profile}).join(' ')
34
29
  const configName = getWidgetUuid()
35
30
  const deployUrl = `s3://${bucket}/${configName}`
36
31
 
@@ -41,12 +36,14 @@ export const deployEmailConfig = async (
41
36
  ${flags}
42
37
  `
43
38
  logTitle(
44
- `Deploying email configuration for “${configName} customer configuration to AWS S3 …`,
39
+ `Deploying email configuration for "${configName}" customer configuration to AWS S3 …`,
45
40
  )
46
41
  logCommand(cmd)
47
42
 
48
43
  try {
49
- withAwsAuthenticate(() => runCommandSync(cmd, {stdout: 'inherit'}), options)
44
+ withAwsAuthenticate(() => runCommandSync(cmd, {stdout: 'inherit'}), {
45
+ profile,
46
+ })
50
47
  } catch (error: any) {
51
48
  halt(error.stderr)
52
49
  }
@@ -54,7 +51,7 @@ export const deployEmailConfig = async (
54
51
  /////////////////////////////////////////////////////////////////////////////
55
52
 
56
53
  logTitle(
57
- `Email configuration for “${configName} customer configuration is deployed.`,
54
+ `Email configuration for "${configName}" customer configuration is deployed.`,
58
55
  )
59
56
  logDataTable({'Deploy URL:': deployUrl})
60
57
 
@@ -62,23 +59,14 @@ export const deployEmailConfig = async (
62
59
 
63
60
  process.on('exit', () => {
64
61
  notify({
65
- title: `deploy-email-config`,
66
- message: `Email configuration for “${configName} customer configuration is deployed.`,
62
+ title: 'deploy-email-config',
63
+ message: `Email configuration for "${configName}" customer configuration is deployed.`,
67
64
  })
68
65
  })
69
66
  }
70
67
 
71
68
  ///////////////////////////////////////////////////////////////////////////////
72
69
 
73
- const assertOptionsValid = (options: DeployEmailConfigOptions) => {
74
- const {bucket, profile} = options
75
-
76
- assertBucketValid(bucket)
77
- assertProfileValid(profile)
78
- }
79
-
80
- ///////////////////////////////////////////////////////////////////////////////
81
-
82
70
  const assertEmailConfigPathExists = (distPath: string) => {
83
71
  if (!existsSync(distPath)) {
84
72
  halt('Email config folder does not exists. Nothing to deploy.', 0)
@@ -87,18 +75,6 @@ const assertEmailConfigPathExists = (distPath: string) => {
87
75
 
88
76
  ///////////////////////////////////////////////////////////////////////////////
89
77
 
90
- const assertBucketValid = (tag: string) => {
91
- const regex = /^[a-zA-Z0-9-_]+$/
92
-
93
- if (!regex.test(tag)) {
94
- halt(
95
- `Flag “--bucket” has forbidden format. Allowed format: ${regex.toString()}.`,
96
- )
97
- }
98
- }
99
-
100
- ///////////////////////////////////////////////////////////////////////////////
101
-
102
78
  const assertIsNotCore = () => {
103
79
  const {ifCore} = getIfCoreFns()
104
80
 
@@ -106,12 +82,3 @@ const assertIsNotCore = () => {
106
82
  halt('You cannot deploy widget email configuration for Tamaro Core.')
107
83
  }
108
84
  }
109
-
110
- ///////////////////////////////////////////////////////////////////////////////
111
-
112
- const prepareFlags = (
113
- flags: string[],
114
- options: DeployEmailConfigOptions,
115
- ): string[] => {
116
- return prepareAwsFlags(flags, options)
117
- }
@@ -1,20 +1,21 @@
1
1
  import {existsSync} from 'fs'
2
2
  import stripIndent from 'strip-indent'
3
+ import {
4
+ assertProfilesPresent,
5
+ type AwsOptions,
6
+ prepareFlags,
7
+ promptProfile,
8
+ withAwsAuthenticate,
9
+ } from 'lib/aws'
10
+ import {halt, runCommandSync} from 'lib/command'
3
11
  import {
4
12
  AWS_CLOUDFRONT_DISTRIBUTION_ID,
5
13
  AWS_S3_BUCKET,
6
14
  CORE_CONFIG_NAME,
7
15
  } from 'lib/constants'
8
- import {
9
- assertProfileValid,
10
- AwsOptions,
11
- withAwsAuthenticate,
12
- prepareFlags as prepareAwsFlags,
13
- } from 'lib/aws'
14
- import {getIfCoreFns, getPaths, getWidgetUuid} from 'lib/resolve'
15
16
  import {logCommand, logDataTable, logTitle} from 'lib/logging'
16
17
  import {notify} from 'lib/notifier'
17
- import {halt, runCommandSync} from 'lib/command'
18
+ import {getIfCoreFns, getPaths, getWidgetUuid} from 'lib/resolve'
18
19
 
19
20
  ///////////////////////////////////////////////////////////////////////////////
20
21
 
@@ -30,6 +31,11 @@ export const deploy = async (options: DeployOptions): Promise<void> => {
30
31
 
31
32
  assertOptionsValid(options)
32
33
  assertDistPathExists(paths.appDist)
34
+ assertProfilesPresent()
35
+
36
+ if (!options.profile) {
37
+ options.profile = await promptProfile()
38
+ }
33
39
 
34
40
  const flags = prepareFlags([], options).join(' ')
35
41
  const {tag} = options
@@ -106,7 +112,7 @@ export const deploy = async (options: DeployOptions): Promise<void> => {
106
112
  const demoPage = `https://${AWS_S3_BUCKET}/${configName}/${tag}/index.html`
107
113
  const entryPoint = `https://${AWS_S3_BUCKET}/${configName}/${tag}/${entryFilename}`
108
114
 
109
- logTitle(`\nBundle for “${configName} is deployed with tag “${tag}”.`)
115
+ logTitle(`\nBundle for "${configName}" is deployed with tag "${tag}".`)
110
116
  logDataTable({
111
117
  'Demo page:': demoPage,
112
118
  'Entry point:': entryPoint,
@@ -118,7 +124,7 @@ export const deploy = async (options: DeployOptions): Promise<void> => {
118
124
  notify({
119
125
  title: 'deploy',
120
126
  message: `
121
- Bundle for “${configName} is deployed with tag “${tag}”.
127
+ Bundle for "${configName}" is deployed with tag "${tag}".
122
128
  Demo page: ${demoPage}
123
129
  Entry point: ${entryPoint}
124
130
  `,
@@ -129,10 +135,9 @@ export const deploy = async (options: DeployOptions): Promise<void> => {
129
135
  ///////////////////////////////////////////////////////////////////////////////
130
136
 
131
137
  const assertOptionsValid = (options: DeployOptions) => {
132
- const {tag, profile} = options
138
+ const {tag} = options
133
139
 
134
140
  assertTagValid(tag)
135
- assertProfileValid(profile)
136
141
  }
137
142
 
138
143
  ///////////////////////////////////////////////////////////////////////////////
@@ -155,13 +160,7 @@ const assertTagValid = (tag: string) => {
155
160
 
156
161
  if (!regex.test(tag)) {
157
162
  halt(
158
- `Flag “--tag has forbidden format. Allowed format: ${regex.toString()}.`,
163
+ `Flag "--tag" has forbidden format. Allowed format: ${regex.toString()}.`,
159
164
  )
160
165
  }
161
166
  }
162
-
163
- ///////////////////////////////////////////////////////////////////////////////
164
-
165
- const prepareFlags = (flags: string[], options: DeployOptions): string[] => {
166
- return prepareAwsFlags(flags, options)
167
- }
@@ -1,9 +1,9 @@
1
1
  import {getPortPromise} from 'portfinder'
2
- import {getIfCoreFns, resolveBin, resolveOwn} from 'lib/resolve'
3
- import {logCommand, logTitle} from 'lib/logging'
4
2
  import {halt, runCommandSync} from 'lib/command'
5
- import {assertEnvValid} from 'lib/env'
3
+ import {applyEnv, assertEnvValid} from 'lib/env'
6
4
  import {assertCrtExists} from 'lib/https'
5
+ import {logCommand, logTitle} from 'lib/logging'
6
+ import {getIfCoreFns, resolveBin, resolveOwn} from 'lib/resolve'
7
7
 
8
8
  ///////////////////////////////////////////////////////////////////////////////
9
9
 
@@ -11,13 +11,16 @@ export type DevOptions = {
11
11
  localCore: boolean
12
12
  https: boolean
13
13
  port: string
14
- env: string
14
+ env?: string
15
15
  }
16
16
 
17
17
  ///////////////////////////////////////////////////////////////////////////////
18
18
 
19
19
  export const dev = async (options: DevOptions): Promise<void> => {
20
+ const {env} = options
21
+
20
22
  assertOptionsValid(options)
23
+ applyEnv(env)
21
24
 
22
25
  const flags = (await prepareFlags([], options)).join(' ')
23
26
  const {ifCore} = getIfCoreFns()
@@ -45,11 +48,11 @@ const assertOptionsValid = (options: DevOptions) => {
45
48
  const {ifCore} = getIfCoreFns()
46
49
 
47
50
  if (ifCore() && localCore) {
48
- halt('Flag “--local-core is redundant if running in Tamaro Core context.')
51
+ halt('Flag "--local-core" is redundant if running in Tamaro Core context.')
49
52
  }
50
53
 
51
54
  if (isNaN(Number(port))) {
52
- halt('Flag “--port should be a number.')
55
+ halt('Flag "--port" should be a number.')
53
56
  }
54
57
 
55
58
  if (https) {
@@ -1,13 +1,14 @@
1
- import {AWS_S3_BUCKET, CORE_CONFIG_NAME} from 'lib/constants'
2
1
  import {
3
- assertProfileValid,
4
- AwsOptions,
2
+ assertProfilesPresent,
3
+ type AwsOptions,
4
+ prepareFlags,
5
+ promptProfile,
5
6
  withAwsAuthenticate,
6
- prepareFlags as prepareAwsFlags,
7
7
  } from 'lib/aws'
8
- import {getIfCoreFns, getWidgetUuid} from 'lib/resolve'
9
- import {logCommand, logTitle} from 'lib/logging'
10
8
  import {halt, runCommandSync} from 'lib/command'
9
+ import {AWS_S3_BUCKET, CORE_CONFIG_NAME} from 'lib/constants'
10
+ import {logCommand, logTitle} from 'lib/logging'
11
+ import {getIfCoreFns, getWidgetUuid} from 'lib/resolve'
11
12
 
12
13
  ///////////////////////////////////////////////////////////////////////////////
13
14
 
@@ -21,6 +22,9 @@ export const listDeployed = async (
21
22
  options: ListDeployedOptions,
22
23
  ): Promise<void> => {
23
24
  assertOptionsValid(options)
25
+ assertProfilesPresent()
26
+
27
+ options.profile = await promptProfile()
24
28
 
25
29
  const flags = prepareFlags([], options).join(' ')
26
30
  const {config} = options
@@ -29,7 +33,7 @@ export const listDeployed = async (
29
33
  const title =
30
34
  !config && ifCore()
31
35
  ? `Listing deployments of Tamaro Core …`
32
- : `Listing deployments of “${configName} customer configuration …`
36
+ : `Listing deployments of "${configName}" customer configuration …`
33
37
 
34
38
  const deployUrl = `s3://${AWS_S3_BUCKET}/${configName}/`
35
39
  const cmd = `aws s3 ls ${deployUrl} ${flags}`
@@ -44,7 +48,7 @@ export const listDeployed = async (
44
48
  const result = withAwsAuthenticate(() => runCommandSync(cmd), options)
45
49
  out = result.stdout
46
50
  } catch (error: any) {
47
- out = error.stdout
51
+ out = error.stdout as string
48
52
 
49
53
  if (error.stderr) {
50
54
  halt(error.stderr)
@@ -66,16 +70,16 @@ export const listDeployed = async (
66
70
  .join('\n')
67
71
  }
68
72
 
73
+ // eslint-disable-next-line no-console
69
74
  console.log(`${text}\n`)
70
75
  }
71
76
 
72
77
  ///////////////////////////////////////////////////////////////////////////////
73
78
 
74
79
  const assertOptionsValid = (options: ListDeployedOptions) => {
75
- const {config, profile} = options
80
+ const {config} = options
76
81
 
77
82
  assertConfigValid(config)
78
- assertProfileValid(profile)
79
83
  }
80
84
 
81
85
  ///////////////////////////////////////////////////////////////////////////////
@@ -89,22 +93,13 @@ const assertConfigValid = (config: string | undefined) => {
89
93
 
90
94
  if (!regex.test(config)) {
91
95
  halt(
92
- `Flag “--config has forbidden format. Allowed format: ${regex.toString()}.`,
96
+ `Flag "--config" has forbidden format. Allowed format: ${regex.toString()}.`,
93
97
  )
94
98
  }
95
99
  }
96
100
 
97
101
  ///////////////////////////////////////////////////////////////////////////////
98
102
 
99
- const prepareFlags = (
100
- flags: string[],
101
- options: ListDeployedOptions,
102
- ): string[] => {
103
- return prepareAwsFlags(flags, options)
104
- }
105
-
106
- ///////////////////////////////////////////////////////////////////////////////
107
-
108
103
  const parseTags = (lines: string[]): string[] => {
109
104
  const regex = /^\s*PRE\s*/
110
105
 
@@ -1,10 +1,10 @@
1
1
  import {resolve} from 'path'
2
2
  import {getPortPromise} from 'portfinder'
3
- import {getIfCoreFns, getPaths} from 'lib/resolve'
4
- import {logCommand, logTitle} from 'lib/logging'
5
3
  import {halt, runCommandSync} from 'lib/command'
6
- import {assertCrtExists} from 'lib/https'
7
4
  import {HTTPS_CRT_FILE, HTTPS_KEY_FILE} from 'lib/constants'
5
+ import {assertCrtExists} from 'lib/https'
6
+ import {logCommand, logTitle} from 'lib/logging'
7
+ import {getIfCoreFns, getPaths} from 'lib/resolve'
8
8
 
9
9
  ///////////////////////////////////////////////////////////////////////////////
10
10
 
@@ -1,10 +1,10 @@
1
- import {Compiler} from 'webpack'
2
- import HtmlWebpackPlugin from 'html-webpack-plugin'
3
1
  import escapeStringRegexp from 'escape-string-regexp'
2
+ import HtmlWebpackPlugin from 'html-webpack-plugin'
3
+ import {type Compiler} from 'webpack'
4
4
 
5
5
  ///////////////////////////////////////////////////////////////////////////////
6
6
 
7
- export type Replacements = {[key: string]: string | undefined}
7
+ export type Replacements = Record<string, string | undefined>
8
8
 
9
9
  ///////////////////////////////////////////////////////////////////////////////
10
10
 
@@ -24,9 +24,9 @@ export class InterpolateHtmlPlugin {
24
24
  const hooks = HtmlWebpackPlugin.getHooks(compilation)
25
25
 
26
26
  hooks.alterAssetTagGroups.tap(PLUGIN_NAME, (assets) => {
27
- const publicUrl = this.replacements['PUBLIC_URL'] ?? ''
28
- const entry = assets.headTags[0].attributes['src'] as string
29
- this.replacements['ENTRY'] = `${publicUrl}${entry}`
27
+ const publicUrl = this.replacements.PUBLIC_URL ?? ''
28
+ const entry = assets.headTags[0].attributes.src as string
29
+ this.replacements.ENTRY = `${publicUrl}${entry}`
30
30
 
31
31
  return assets
32
32
  })
@@ -36,7 +36,7 @@ export class InterpolateHtmlPlugin {
36
36
  Object.entries(this.replacements).forEach(([key, value]) => {
37
37
  data.html = data.html.replace(
38
38
  new RegExp(`%${escapeStringRegexp(key)}%`, 'g'),
39
- value || '',
39
+ value ?? '',
40
40
  )
41
41
  })
42
42
 
package/src/lib/aws.ts CHANGED
@@ -1,11 +1,17 @@
1
1
  import {execaCommandSync} from 'execa'
2
+ // eslint-disable-next-line import/no-named-as-default
3
+ import prompts from 'prompts'
2
4
  import stripIndent from 'strip-indent'
3
5
  import {halt} from 'lib/command'
6
+ import {
7
+ AWS_S3_EMAIL_CONFIG_PROD_BUCKET,
8
+ AWS_S3_EMAIL_CONFIG_STAGE_BUCKET,
9
+ } from 'lib/constants'
4
10
 
5
11
  ///////////////////////////////////////////////////////////////////////////////
6
12
 
7
13
  export type AwsOptions = {
8
- profile: string
14
+ profile?: string
9
15
  }
10
16
 
11
17
  ///////////////////////////////////////////////////////////////////////////////
@@ -45,42 +51,28 @@ export const isSetEnv = () => {
45
51
 
46
52
  ///////////////////////////////////////////////////////////////////////////////
47
53
 
48
- export const assertProfileValid = (profile: string) => {
54
+ export const assertProfilesPresent = () => {
49
55
  if (isSetEnv()) {
50
56
  return
51
57
  }
52
58
 
53
59
  const profiles = getAvailableProfiles()
54
60
 
55
- if (!profiles.includes(profile)) {
56
- console.log(`Using AWS profile: “${profile}”`)
57
-
58
- let message
59
-
60
- if (profiles.length === 0) {
61
- message = stripIndent(`
61
+ if (profiles.length === 0) {
62
+ halt(
63
+ stripIndent(`
62
64
  No AWS profiles found.
63
- `)
64
- } else {
65
- message = stripIndent(`
66
- AWS profile “${profile}” has not been found.
67
- Available profiles are: ${profiles.map((v) => `“${v}”`).join(', ')}.
68
- `)
69
- }
70
-
71
- message += stripIndent(`
72
- Run “aws configure sso” to set up SSO-enabled profile.
73
- Check the wiki for more information:
74
- https://raisenow.atlassian.net/wiki/x/lIrWvg
75
- `)
76
-
77
- halt(message)
65
+ Run "aws configure sso" to set up SSO-enabled profile.
66
+ Check the wiki for more information:
67
+ https://raisenow.atlassian.net/wiki/x/lIrWvg
68
+ `),
69
+ )
78
70
  }
79
71
  }
80
72
 
81
73
  ///////////////////////////////////////////////////////////////////////////////
82
74
 
83
- const getAvailableProfiles = (): string[] => {
75
+ export const getAvailableProfiles = (): string[] => {
84
76
  const {stdout} = execaCommandSync('aws configure list-profiles')
85
77
 
86
78
  return stdout.split('\n')
@@ -105,6 +97,7 @@ const login = (options: AwsOptions) => {
105
97
 
106
98
  try {
107
99
  execaCommandSync(`aws sso login ${flags}`, {stdio: 'inherit'})
100
+ // eslint-disable-next-line no-console
108
101
  console.log('')
109
102
  } catch (error) {
110
103
  halt('Login failed.')
@@ -119,9 +112,62 @@ export const prepareFlags = (
119
112
  ): string[] => {
120
113
  const {profile} = options
121
114
 
122
- if (!isSetEnv()) {
115
+ if (!isSetEnv() && profile) {
123
116
  flags.push(`--profile ${profile}`)
124
117
  }
125
118
 
126
119
  return flags
127
120
  }
121
+
122
+ ///////////////////////////////////////////////////////////////////////////////
123
+
124
+ export const promptProfile = async (): Promise<string | undefined> => {
125
+ if (isSetEnv()) {
126
+ return
127
+ }
128
+
129
+ const profiles = getAvailableProfiles()
130
+ .filter((v) => !!v)
131
+ .map((v) => ({title: v, value: v}))
132
+
133
+ const {profile} = await prompts(
134
+ [
135
+ {
136
+ type: 'select',
137
+ name: 'profile',
138
+ message: 'Select AWS profile',
139
+ choices: profiles,
140
+ },
141
+ ],
142
+ {
143
+ onCancel: () => process.exit(1),
144
+ },
145
+ )
146
+
147
+ return profile
148
+ }
149
+
150
+ ///////////////////////////////////////////////////////////////////////////////
151
+
152
+ export const promptBucket = async (): Promise<string> => {
153
+ const buckets = [
154
+ {title: 'stage', value: AWS_S3_EMAIL_CONFIG_STAGE_BUCKET},
155
+ {title: 'prod', value: AWS_S3_EMAIL_CONFIG_PROD_BUCKET},
156
+ ]
157
+
158
+ const {bucket} = await prompts(
159
+ [
160
+ {
161
+ type: 'select',
162
+ name: 'bucket',
163
+ message: 'Select environment',
164
+ choices: buckets,
165
+ },
166
+ ],
167
+ {
168
+ onCancel: () => process.exit(1),
169
+ },
170
+ )
171
+
172
+ return bucket
173
+ }
@@ -1,4 +1,8 @@
1
- import {execaCommandSync, ExecaSyncReturnValue, SyncOptions} from 'execa'
1
+ import {
2
+ execaCommandSync,
3
+ type ExecaSyncReturnValue,
4
+ type SyncOptions,
5
+ } from 'execa'
2
6
  import {logError} from 'lib/logging'
3
7
 
4
8
  ///////////////////////////////////////////////////////////////////////////////
@@ -1,7 +1,8 @@
1
1
  export const DEFAULT_TAG = 'latest'
2
2
  export const DEFAULT_PORT = 1234
3
3
  export const DEFAULT_AWS_PROFILE = 'payments-prod-cs-deployer'
4
- export const DEFAULT_AWS_S3_EMAIL_CONFIG_BUCKET = 'rnw-email-service'
4
+ export const AWS_S3_EMAIL_CONFIG_PROD_BUCKET = 'rnw-email-service'
5
+ export const AWS_S3_EMAIL_CONFIG_STAGE_BUCKET = 'rnw-stage-email-service'
5
6
  export const AWS_S3_BUCKET = 'tamaro.raisenow.com'
6
7
  export const CORE_CONFIG_NAME = 'tamaro-core'
7
8
  export const AWS_CLOUDFRONT_DISTRIBUTION_ID = 'EHJ1OM458YQ0I'