@raisenow/tamaro-cli 1.1.7 → 1.1.9

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,88 +0,0 @@
1
- import {getPortPromise} from 'portfinder'
2
- import {halt, runCommandSync} from 'lib/command'
3
- import {applyEnv, assertEnvValid} from 'lib/env'
4
- import {assertCrtExists} from 'lib/https'
5
- import {logCommand, logTitle} from 'lib/logging'
6
- import {getIfCoreFns, resolveBin, resolveOwn} from 'lib/resolve'
7
-
8
- ///////////////////////////////////////////////////////////////////////////////
9
-
10
- export type DevOptions = {
11
- localCore: boolean
12
- https: boolean
13
- port: string
14
- env?: string
15
- nolint: boolean
16
- }
17
-
18
- ///////////////////////////////////////////////////////////////////////////////
19
-
20
- export const dev = async (options: DevOptions): Promise<void> => {
21
- const {env} = options
22
-
23
- applyEnv(env)
24
- assertOptionsValid(options)
25
-
26
- const flags = await prepareFlags(options)
27
- const {ifCore} = getIfCoreFns()
28
- const title = ifCore(
29
- 'Running dev web-server for Tamaro Core …',
30
- 'Running dev web-server for customer configuration …',
31
- )
32
- const wpBin = resolveBin('webpack')
33
- const wpConfig = resolveOwn('dist/webpack.config.js')
34
- const cmd = `
35
- ${wpBin} serve
36
- --config ${wpConfig}
37
- ${flags}
38
- `
39
-
40
- logTitle(title)
41
- logCommand(cmd)
42
- runCommandSync(cmd, {stdio: 'inherit'})
43
- }
44
-
45
- ///////////////////////////////////////////////////////////////////////////////
46
-
47
- const assertOptionsValid = (options: DevOptions) => {
48
- const {localCore, https, port, env} = options
49
- const {ifCore} = getIfCoreFns()
50
-
51
- if (ifCore() && localCore) {
52
- halt('Flag "--local-core" is redundant if running in Tamaro Core context.')
53
- }
54
-
55
- if (isNaN(Number(port))) {
56
- halt('Flag "--port" should be a number.')
57
- }
58
-
59
- if (https) {
60
- assertCrtExists()
61
- }
62
-
63
- assertEnvValid(env)
64
- }
65
-
66
- ///////////////////////////////////////////////////////////////////////////////
67
-
68
- const prepareFlags = async (options: DevOptions) => {
69
- const flags: string[] = []
70
- const {localCore, port: defaultPort, https, nolint} = options
71
- const port = await getPortPromise({port: Number(defaultPort)})
72
-
73
- if (localCore) {
74
- flags.push('--env localCore')
75
- }
76
-
77
- flags.push(`--port ${port}`)
78
-
79
- if (https) {
80
- flags.push('--env https')
81
- }
82
-
83
- if (nolint) {
84
- flags.push('--env nolint')
85
- }
86
-
87
- return flags.join(' ')
88
- }
@@ -1,117 +0,0 @@
1
- import {
2
- assertProfilesPresent,
3
- assertProfileValid,
4
- awsAuthenticate,
5
- type AwsOptions,
6
- prepareFlags,
7
- promptProfile,
8
- } from 'lib/aws'
9
- import {halt, runCommandSync} from 'lib/command'
10
- import {AWS_S3_BUCKET_TAMARO, CORE_CONFIG_NAME} from 'lib/constants'
11
- import {logCommand, logTitle} from 'lib/logging'
12
- import {getIfCoreFns, getWidgetUuid} from 'lib/resolve'
13
-
14
- ///////////////////////////////////////////////////////////////////////////////
15
-
16
- export type ListDeployedOptions = AwsOptions & {
17
- config?: string
18
- }
19
-
20
- ///////////////////////////////////////////////////////////////////////////////
21
-
22
- export const listDeployed = async (
23
- options: ListDeployedOptions,
24
- ): Promise<void> => {
25
- if (!options.profile && !options.ci) {
26
- assertProfilesPresent()
27
- options.profile = await promptProfile()
28
- }
29
-
30
- assertOptionsValid(options)
31
- awsAuthenticate(options)
32
-
33
- const flags = prepareFlags(options)
34
- const {config} = options
35
- const {ifCore} = getIfCoreFns()
36
- const configName = config ?? ifCore(CORE_CONFIG_NAME, getWidgetUuid())
37
- const title =
38
- !config && ifCore()
39
- ? `Listing deployments of Tamaro Core …`
40
- : `Listing deployments of "${configName}" customer configuration …`
41
-
42
- const deployUrl = `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/`
43
- const cmd = `aws s3 ls ${deployUrl} ${flags}`
44
- let out = ''
45
-
46
- logTitle(title)
47
- logCommand(cmd)
48
-
49
- // If customer configuration folder does not exist on AWS S3,
50
- // command fails with blank stderr, so we need to handle this case.
51
- try {
52
- const result = runCommandSync(cmd)
53
- out = result.stdout as string
54
- } catch (error: any) {
55
- out = error.stdout as string
56
-
57
- if (error.stderr) {
58
- halt(error.stderr)
59
- }
60
- }
61
-
62
- const lines = out.split('\n')
63
- const tags = parseTags(lines)
64
- let text = ''
65
-
66
- if (tags.length === 0) {
67
- text = 'No deployments found.'
68
- } else {
69
- text = tags
70
- .map((tag, idx) => {
71
- // prettier-ignore
72
- return `${idx + 1}. https://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/index.html`
73
- })
74
- .join('\n')
75
- }
76
-
77
- // eslint-disable-next-line no-console
78
- console.log(`${text}\n`)
79
- }
80
-
81
- ///////////////////////////////////////////////////////////////////////////////
82
-
83
- const assertOptionsValid = (options: ListDeployedOptions) => {
84
- const {config, profile} = options
85
-
86
- assertConfigValid(config)
87
-
88
- if (profile) {
89
- assertProfileValid(profile)
90
- }
91
- }
92
-
93
- ///////////////////////////////////////////////////////////////////////////////
94
-
95
- const assertConfigValid = (config: string | undefined) => {
96
- if (!config) {
97
- return
98
- }
99
-
100
- const regex = /^[a-zA-Z0-9-_]+$/
101
-
102
- if (!regex.test(config)) {
103
- halt(
104
- `Flag "--config" has forbidden format. Allowed format: ${regex.toString()}.`,
105
- )
106
- }
107
- }
108
-
109
- ///////////////////////////////////////////////////////////////////////////////
110
-
111
- const parseTags = (lines: string[]): string[] => {
112
- const regex = /^\s*PRE\s*/
113
-
114
- return lines
115
- .filter((line) => regex.test(line))
116
- .map((line) => line.replace(regex, '').replace(/\/$/, ''))
117
- }
@@ -1,69 +0,0 @@
1
- import {resolve} from 'path'
2
- import {getPortPromise} from 'portfinder'
3
- import {halt, runCommandSync} from 'lib/command'
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
-
9
- ///////////////////////////////////////////////////////////////////////////////
10
-
11
- export type ServeOptions = {
12
- port: string
13
- https: boolean
14
- }
15
-
16
- ///////////////////////////////////////////////////////////////////////////////
17
-
18
- export const serve = async (options: ServeOptions): Promise<void> => {
19
- assertOptionsValid(options)
20
-
21
- const flags = await prepareFlags(options)
22
- const {ifCore} = getIfCoreFns()
23
- const paths = getPaths(ifCore)
24
- const cmd = `
25
- npx -y serve ${paths.appDist}
26
- --cors
27
- ${flags}
28
- `
29
-
30
- logTitle(`Running web-server for pre-built bundle …`)
31
- logCommand(cmd)
32
- runCommandSync(cmd, {stdio: 'inherit'})
33
- }
34
-
35
- ///////////////////////////////////////////////////////////////////////////////
36
-
37
- const assertOptionsValid = (options: ServeOptions) => {
38
- const {port, https} = options
39
-
40
- if (isNaN(Number(port))) {
41
- halt('Flag "--port" should be a number.')
42
- }
43
-
44
- if (https) {
45
- assertCrtExists()
46
- }
47
- }
48
-
49
- ///////////////////////////////////////////////////////////////////////////////
50
-
51
- const prepareFlags = async (options: ServeOptions) => {
52
- const flags: string[] = []
53
- const {port: defaultPort, https} = options
54
- const port = await getPortPromise({port: Number(defaultPort)})
55
-
56
- flags.push(`-p ${port}`)
57
-
58
- if (https) {
59
- const {ifCore} = getIfCoreFns()
60
- const paths = getPaths(ifCore)
61
- const certFile = resolve(paths.root, HTTPS_CRT_FILE)
62
- const keyFile = resolve(paths.root, HTTPS_KEY_FILE)
63
-
64
- flags.push(`--ssl-cert ${certFile}`)
65
- flags.push(`--ssl-key ${keyFile}`)
66
- }
67
-
68
- return flags.join(' ')
69
- }
@@ -1,47 +0,0 @@
1
- import escapeStringRegexp from 'escape-string-regexp'
2
- import HtmlWebpackPlugin from 'html-webpack-plugin'
3
- import {type Compiler} from 'webpack'
4
-
5
- ///////////////////////////////////////////////////////////////////////////////
6
-
7
- export type Replacements = Record<string, string | undefined>
8
-
9
- ///////////////////////////////////////////////////////////////////////////////
10
-
11
- const PLUGIN_NAME = 'InterpolateHtmlPlugin'
12
-
13
- ///////////////////////////////////////////////////////////////////////////////
14
-
15
- export class InterpolateHtmlPlugin {
16
- replacements: Replacements
17
-
18
- constructor(replacements: Replacements) {
19
- this.replacements = {...replacements}
20
- }
21
-
22
- apply(compiler: Compiler): void {
23
- compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
24
- const hooks = HtmlWebpackPlugin.getHooks(compilation)
25
-
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}`
30
-
31
- return assets
32
- })
33
-
34
- hooks.afterTemplateExecution.tap(PLUGIN_NAME, (data) => {
35
- // Run HTML through a series of user-specified string replacements.
36
- Object.entries(this.replacements).forEach(([key, value]) => {
37
- data.html = data.html.replace(
38
- new RegExp(`%${escapeStringRegexp(key)}%`, 'g'),
39
- value ?? '',
40
- )
41
- })
42
-
43
- return data
44
- })
45
- })
46
- }
47
- }
package/src/lib/aws.ts DELETED
@@ -1,138 +0,0 @@
1
- import {execaCommandSync} from 'execa'
2
- // eslint-disable-next-line import/no-named-as-default
3
- import prompts from 'prompts'
4
- import stripIndent from 'strip-indent'
5
- import {halt} from 'lib/command'
6
-
7
- ///////////////////////////////////////////////////////////////////////////////
8
-
9
- export type AwsOptions = {
10
- profile?: string
11
- ci?: boolean
12
- }
13
-
14
- ///////////////////////////////////////////////////////////////////////////////
15
-
16
- export const awsAuthenticate = (options: AwsOptions) => {
17
- if (options.ci) {
18
- /**
19
- * Don't try to authenticate the user.
20
- * This is done when CLI is running within an automated system like pipeline,
21
- * In this case "AWS_ACCESS_KEY_ID" and "AWS_SECRET_ACCESS_KEY" are used instead of "AWS_PROFILE".
22
- */
23
- return
24
- }
25
-
26
- try {
27
- checkIdentity(options)
28
- } catch (error) {
29
- login(options)
30
- }
31
- }
32
-
33
- ///////////////////////////////////////////////////////////////////////////////
34
-
35
- export const assertProfilesPresent = () => {
36
- const profiles = getAvailableProfiles()
37
-
38
- if (profiles.length === 0) {
39
- halt(
40
- stripIndent(`
41
- No AWS profiles found.
42
- Run "aws configure sso" to set up SSO-enabled profile.
43
- Check the wiki for more information:
44
- https://raisenow.atlassian.net/wiki/x/lIrWvg
45
- `),
46
- )
47
- }
48
- }
49
-
50
- ///////////////////////////////////////////////////////////////////////////////
51
-
52
- export const getAvailableProfiles = (): string[] => {
53
- const {stdout} = execaCommandSync('aws configure list-profiles')
54
-
55
- return stdout.split('\n')
56
- }
57
-
58
- ///////////////////////////////////////////////////////////////////////////////
59
-
60
- /**
61
- * Returns caller identity data (output is ignored here).
62
- * Fails in case of expired SSO session.
63
- */
64
- const checkIdentity = (options: AwsOptions) => {
65
- const flags = prepareFlags(options)
66
-
67
- execaCommandSync(`aws sts get-caller-identity ${flags}`)
68
- }
69
-
70
- ///////////////////////////////////////////////////////////////////////////////
71
-
72
- /**
73
- * Opens SSO authentication page in the browser.
74
- * Fails if user canceled authentication process.
75
- */
76
- const login = (options: AwsOptions) => {
77
- const flags = prepareFlags(options)
78
-
79
- try {
80
- execaCommandSync(`aws sso login ${flags}`, {stdio: 'inherit'})
81
- // eslint-disable-next-line no-console
82
- console.log('')
83
- } catch (error) {
84
- halt('Login failed.')
85
- }
86
- }
87
-
88
- ///////////////////////////////////////////////////////////////////////////////
89
-
90
- export const prepareFlags = (options: AwsOptions) => {
91
- const flags: string[] = []
92
- const {profile} = options
93
-
94
- if (profile) {
95
- flags.push(`--profile ${profile}`)
96
- }
97
-
98
- return flags.join(' ')
99
- }
100
-
101
- ///////////////////////////////////////////////////////////////////////////////
102
-
103
- export const assertProfileValid = (profile: string) => {
104
- const profiles = getAvailableProfiles()
105
-
106
- if (!profiles.includes(profile)) {
107
- halt(
108
- stripIndent(`
109
- AWS profile "${profile}" is not found.
110
- Available profiles are: ${profiles.map((v) => `"${v}"`).join(', ')}.
111
- `),
112
- )
113
- }
114
- }
115
-
116
- ///////////////////////////////////////////////////////////////////////////////
117
-
118
- export const promptProfile = async (): Promise<string | undefined> => {
119
- const profiles = getAvailableProfiles()
120
- .filter((v) => !!v)
121
- .map((v) => ({title: v, value: v}))
122
-
123
- const {profile} = await prompts(
124
- [
125
- {
126
- type: 'select',
127
- name: 'profile',
128
- message: 'Select AWS profile',
129
- choices: profiles,
130
- },
131
- ],
132
- {
133
- onCancel: () => halt(),
134
- },
135
- )
136
-
137
- return profile
138
- }
@@ -1,27 +0,0 @@
1
- import {execaCommandSync, type SyncOptions, type SyncResult} from 'execa'
2
- import {logError} from 'lib/logging'
3
-
4
- ///////////////////////////////////////////////////////////////////////////////
5
-
6
- export const halt = (message?: string) => {
7
- logError(message)
8
- process.exit(1)
9
- }
10
-
11
- ///////////////////////////////////////////////////////////////////////////////
12
-
13
- export const prepareCommand = (cmd: string): string => {
14
- return cmd
15
- .replace(/\n/gm, ' ')
16
- .replace(/[ \t]{2,}/gm, ' ')
17
- .trim()
18
- }
19
-
20
- ///////////////////////////////////////////////////////////////////////////////
21
-
22
- export const runCommandSync = (
23
- cmd: string,
24
- options?: SyncOptions,
25
- ): SyncResult => {
26
- return execaCommandSync(prepareCommand(cmd), options)
27
- }
@@ -1,9 +0,0 @@
1
- export const DEFAULT_TAG = 'latest'
2
- export const DEFAULT_PORT = 1234
3
- export const AWS_S3_BUCKET_TAMARO = 'tamaro.raisenow.com'
4
- export const AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE = 'rnw-stage-email-service'
5
- export const AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD = 'rnw-email-service'
6
- export const CORE_CONFIG_NAME = 'tamaro-core'
7
- export const AWS_CLOUDFRONT_DISTRIBUTION_ID = 'EHJ1OM458YQ0I'
8
- export const HTTPS_CRT_FILE = 'localhost.crt'
9
- export const HTTPS_KEY_FILE = 'localhost.key'
package/src/lib/env.ts DELETED
@@ -1,176 +0,0 @@
1
- import {createRequire} from 'module'
2
- import {basename} from 'path'
3
- import {config as dotenvConfig} from 'dotenv'
4
- import {expand as dotenvExpand} from 'dotenv-expand'
5
- import {globSync} from 'glob'
6
- import stripIndent from 'strip-indent'
7
- import type {IfUtilsFn} from 'webpack-config-utils'
8
- import {halt} from 'lib/command'
9
- import {getIfCoreFns, getPaths, getWidgetUuid, resolveApp} from 'lib/resolve'
10
-
11
- ///////////////////////////////////////////////////////////////////////////////
12
-
13
- export type EnvVars = Record<string, string | undefined>
14
-
15
- ///////////////////////////////////////////////////////////////////////////////
16
-
17
- const require = createRequire(import.meta.url)
18
-
19
- ///////////////////////////////////////////////////////////////////////////////
20
-
21
- export const getEnvVars = (
22
- files: string[],
23
- ifMin: IfUtilsFn,
24
- ifCore: IfUtilsFn,
25
- ifLocalCore: IfUtilsFn,
26
- ifHttps: IfUtilsFn,
27
- ) => {
28
- const filePath = files.find((file) => basename(file) === '.env')
29
-
30
- if (filePath) {
31
- dotenvExpand(dotenvConfig({path: filePath}))
32
- }
33
-
34
- process.env.NODE_ENV ??= ifMin('production', 'development')
35
- process.env.BABEL_ENV ??= ifMin('production', 'development')
36
- process.env.EXPOSE_VAR ??= ifCore('rnw.tamaroCore', 'rnw.tamaro')
37
- process.env.ELEMENT_ATTRIBUTE_DATA_WIDGET ??= ifCore(
38
- 'rnw-tamaro-core',
39
- 'rnw-tamaro',
40
- )
41
- process.env.WEBPACK_UNIQUE_NAME ??= ifCore('RnwTamaroCore', 'RnwTamaro')
42
- process.env.BUILD_DATE = new Date().toISOString()
43
- process.env.PUBLIC_URL ??= ''
44
- process.env.HMR_ENABLED ??= ifMin('false', 'true')
45
-
46
- if (ifCore()) {
47
- process.env.PRODUCT_NAME = 'tamaro'
48
-
49
- const corePkgPath = resolveApp('package.json')
50
- // eslint-disable-next-line @typescript-eslint/no-var-requires
51
- const {version} = require(corePkgPath)
52
-
53
- process.env.PRODUCT_VERSION = version
54
- }
55
-
56
- if (!ifCore()) {
57
- process.env.WIDGET_UUID = getWidgetUuid()
58
-
59
- if (ifLocalCore()) {
60
- const protocol = ifHttps() ? 'https' : 'http'
61
- process.env.CORE_URL = `${protocol}://localhost:1234/index.js`
62
- } else {
63
- let version = process.env.CORE_VERSION
64
- version = version ? `@${version}` : ''
65
-
66
- // process.env.CORE_URL ??= `https://cdn.jsdelivr.net/npm/@raisenow/tamaro-core${version}/dist/index.js`
67
- process.env.CORE_URL ??= `https://fastly.jsdelivr.net/npm/@raisenow/tamaro-core${version}/dist/index.js`
68
- }
69
- }
70
-
71
- // var names which will be exposed along with "PUBLIC_*" var names
72
- const varNames = [
73
- // common
74
- 'NODE_ENV',
75
- 'BABEL_ENV',
76
- 'EXPOSE_VAR',
77
- 'ELEMENT_ATTRIBUTE_DATA_WIDGET',
78
- 'WEBPACK_UNIQUE_NAME',
79
- 'BUILD_DATE',
80
- 'PUBLIC_URL',
81
-
82
- // core
83
- 'PRODUCT_NAME',
84
- 'PRODUCT_VERSION',
85
-
86
- // config
87
- 'WIDGET_UUID',
88
- 'CORE_URL',
89
- 'CORE_VERSION', // should be explicitly specified in each config
90
-
91
- // epik (obsolete since Tamaro v2.5.0)
92
- 'EPP_API_KEY_DEFAULT',
93
- 'EPP_API_URL_STAGE',
94
- 'EPP_API_URL_PROD',
95
- 'EPMS_API_URL_STAGE',
96
- 'EPMS_API_URL_PROD',
97
- 'EPP_PROXY_URL_STAGE',
98
- 'EPP_PROXY_URL_PROD',
99
- 'EPMS_PROXY_URL_STAGE',
100
- 'EPMS_PROXY_URL_PROD',
101
- 'EPMS_TWINT_CHECKOUT_URL_STAGE',
102
- 'EPMS_TWINT_CHECKOUT_URL_PROD',
103
- ]
104
-
105
- // Collect all vars which names start from "PUBLIC_" or present in "varNames" array
106
- const raw = Object.keys(process.env)
107
- .filter((key) => key.startsWith('PUBLIC_') || varNames.includes(key))
108
- .reduce<EnvVars>((env, key) => {
109
- env[key] = process.env[key]
110
-
111
- return env
112
- }, {})
113
-
114
- // Stringify all values so we can feed into webpack DefinePlugin
115
- const stringified = {
116
- 'process.env': Object.keys(raw).reduce<EnvVars>((env, key) => {
117
- env[key] = JSON.stringify(raw[key])
118
-
119
- return env
120
- }, {}),
121
- }
122
-
123
- return {raw, stringified}
124
- }
125
-
126
- ///////////////////////////////////////////////////////////////////////////////
127
-
128
- export const assertEnvValid = (env?: string) => {
129
- const {ifCore} = getIfCoreFns()
130
- const paths = getPaths(ifCore)
131
- const files = globSync(paths.appEnv)
132
- const envs = files
133
- .map((file) => basename(file).replace(/^\.env\./, ''))
134
- .map((file) => basename(file).replace(/^\.env$/, ''))
135
- .filter((v) => !!v)
136
-
137
- if (envs.length === 0) {
138
- if (env) {
139
- // There are no ".env.<env>" files, so we must ignore "--env" flag
140
- // eslint-disable-next-line no-console
141
- console.log('Flag "--env" is ignored.')
142
- }
143
- }
144
-
145
- if (envs.length !== 0) {
146
- if (!env) {
147
- // There are some ".env.<env>" files, but no "--env" flag is specified, so we must require it
148
- halt('Flag "--env" is required.')
149
- }
150
-
151
- if (env && !envs.includes(env)) {
152
- // There are some ".env.<env>" files, but specified "--env" flag is incorrect
153
- halt(
154
- stripIndent(`
155
- Flag "--env" has wrong value.
156
- Available values are: ${envs.map((v) => `"${v}"`).join(', ')}.
157
- `),
158
- )
159
- }
160
- }
161
- }
162
-
163
- export const applyEnv = (env?: string) => {
164
- if (!env) {
165
- return
166
- }
167
-
168
- const {ifCore} = getIfCoreFns()
169
- const paths = getPaths(ifCore)
170
- const files = globSync(paths.appEnv)
171
- const filePath = files.find((file) => basename(file) === `.env.${env}`)
172
-
173
- if (filePath) {
174
- dotenvExpand(dotenvConfig({path: filePath}))
175
- }
176
- }