@mono-labs/cli 0.0.5
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 +86 -0
- package/bin/haste.js +2 -0
- package/index.js +3 -0
- package/lib/app.js +28 -0
- package/lib/commands/build/index.js +71 -0
- package/lib/commands/build-process/index.js +412 -0
- package/lib/commands/deploy/index.js +44 -0
- package/lib/commands/destroy.js +27 -0
- package/lib/commands/dev/dev-editor.js +265 -0
- package/lib/commands/dev/index.js +22 -0
- package/lib/commands/dev/ngrok.js +88 -0
- package/lib/commands/generate/generateSeed.js +224 -0
- package/lib/commands/generate/index.js +30 -0
- package/lib/commands/init/index.js +37 -0
- package/lib/commands/loadFromRoot.js +38 -0
- package/lib/commands/prune/index.js +12 -0
- package/lib/commands/prune/prune.js +48 -0
- package/lib/commands/reset.js +31 -0
- package/lib/commands/seed/import.js +31 -0
- package/lib/commands/seed/index.js +12 -0
- package/lib/commands/squash/index.js +8 -0
- package/lib/commands/squash/squash.js +150 -0
- package/lib/commands/submit/index.js +38 -0
- package/lib/commands/test/index.js +251 -0
- package/lib/commands/update/eas.js +39 -0
- package/lib/commands/update/index.js +92 -0
- package/lib/config.js +4 -0
- package/lib/index.js +19 -0
- package/lib/migrations/index.js +2 -0
- package/lib/migrations/v0.js +3 -0
- package/lib/migrations/v1.js +4 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Haste CLI Tool
|
|
2
|
+
|
|
3
|
+
A fast and efficient CLI tool for building and deploying projects.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Local Development
|
|
8
|
+
```bash
|
|
9
|
+
# Install dependencies
|
|
10
|
+
yarn install
|
|
11
|
+
|
|
12
|
+
# Test the CLI locally
|
|
13
|
+
yarn start --help
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Global Installation
|
|
17
|
+
```bash
|
|
18
|
+
# Install globally using yarn
|
|
19
|
+
yarn global add .
|
|
20
|
+
|
|
21
|
+
# Or link for development
|
|
22
|
+
yarn link
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Build Command
|
|
28
|
+
```bash
|
|
29
|
+
# Basic build
|
|
30
|
+
haste build
|
|
31
|
+
|
|
32
|
+
# Build with specific environment
|
|
33
|
+
haste build --env production
|
|
34
|
+
|
|
35
|
+
# Build with watch mode
|
|
36
|
+
haste build --watch
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Deploy Command
|
|
40
|
+
```bash
|
|
41
|
+
# Basic deploy
|
|
42
|
+
haste deploy
|
|
43
|
+
|
|
44
|
+
# Deploy to specific environment
|
|
45
|
+
haste deploy --env staging
|
|
46
|
+
|
|
47
|
+
# Force deployment
|
|
48
|
+
haste deploy --force
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Available Commands
|
|
52
|
+
|
|
53
|
+
- `haste build` - Build the project
|
|
54
|
+
- `haste deploy` - Deploy the project
|
|
55
|
+
- `haste --help` - Show help information
|
|
56
|
+
- `haste --version` - Show version information
|
|
57
|
+
|
|
58
|
+
## Development
|
|
59
|
+
|
|
60
|
+
### Project Structure
|
|
61
|
+
```
|
|
62
|
+
haste/
|
|
63
|
+
├── bin/
|
|
64
|
+
│ └── haste.js # Main CLI entry point
|
|
65
|
+
├── lib/
|
|
66
|
+
│ └── commands/
|
|
67
|
+
│ ├── build.js # Build command implementation
|
|
68
|
+
│ └── deploy.js # Deploy command implementation
|
|
69
|
+
├── package.json
|
|
70
|
+
└── README.md
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Adding New Commands
|
|
74
|
+
|
|
75
|
+
1. Create a new file in `lib/commands/`
|
|
76
|
+
2. Export an `execute` function
|
|
77
|
+
3. Add the command to `bin/haste.js`
|
|
78
|
+
|
|
79
|
+
### Dependencies
|
|
80
|
+
|
|
81
|
+
- **commander** - Command-line argument parsing
|
|
82
|
+
- **chalk** - Terminal string styling
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
package/bin/haste.js
ADDED
package/index.js
ADDED
package/lib/app.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
|
|
3
|
+
import { STAGING_URL } from './config.js'
|
|
4
|
+
|
|
5
|
+
const version = '0.0.1'
|
|
6
|
+
export const program = new Command()
|
|
7
|
+
|
|
8
|
+
program.name('haste').description('CLI to manage Haste project').version(version)
|
|
9
|
+
const EXPO_PUBLIC_API_URL =
|
|
10
|
+
(process.env.EXPO_PUBLIC_API_URL && process.env.EXPO_PUBLIC_API_URL.length > 0) || STAGING_URL
|
|
11
|
+
const CLIENT_NAME = process.env.CLIENT_NAME || 'jawdrop'
|
|
12
|
+
|
|
13
|
+
export const generateEnvValues = (
|
|
14
|
+
forceProd = false,
|
|
15
|
+
ngrokUrl = 'localhost:3000',
|
|
16
|
+
useAtlas = false,
|
|
17
|
+
) => {
|
|
18
|
+
return {
|
|
19
|
+
...process.env,
|
|
20
|
+
EXPO_PUBLIC_API_URL,
|
|
21
|
+
|
|
22
|
+
CLIENT_NAME,
|
|
23
|
+
EXPO_FORCE_PROD: forceProd,
|
|
24
|
+
EXPO_PRIVATE_API_URL: ngrokUrl,
|
|
25
|
+
EXPO_UNSTABLE_ATLAS: useAtlas,
|
|
26
|
+
EXPO_CLIENT_NAME: CLIENT_NAME,
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { spawn } from 'child_process'
|
|
2
|
+
|
|
3
|
+
import { program } from '../../app.js'
|
|
4
|
+
import { generateEnvValues } from '../../app.js'
|
|
5
|
+
import { STAGING_URL } from '../../config.js'
|
|
6
|
+
|
|
7
|
+
const allowedPlatforms = ['ios', 'android']
|
|
8
|
+
function selectPlatform(platform) {
|
|
9
|
+
if (!allowedPlatforms.includes(platform))
|
|
10
|
+
throw new Error(`Invalid platform selected, must be one of ${allowedPlatforms.join(', ')}`)
|
|
11
|
+
return platform
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function selectProfile(dev, preview, prod, profile) {
|
|
15
|
+
if (profile && (dev || preview || prod)) {
|
|
16
|
+
throw new Error('Conflict between profile and dev/preview/prod')
|
|
17
|
+
}
|
|
18
|
+
if (dev) return 'development'
|
|
19
|
+
if (preview) return 'preview'
|
|
20
|
+
if (prod) return 'production'
|
|
21
|
+
if (profile) return profile
|
|
22
|
+
return 'development'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
program
|
|
26
|
+
.command('build')
|
|
27
|
+
.description('Execute eas build command')
|
|
28
|
+
.option('-d', 'Build to target development profile')
|
|
29
|
+
.option('-p', 'Build to target preview profile')
|
|
30
|
+
.option('--live', 'Build to target production profile')
|
|
31
|
+
.option('--profile <string>', 'Build profile to use')
|
|
32
|
+
.option('--os, --platform <string>', 'Platform to build', 'ios')
|
|
33
|
+
.option('-f, --force', 'Force build to target production server')
|
|
34
|
+
.action((str, options) => {
|
|
35
|
+
console.log('its me')
|
|
36
|
+
const platform = selectPlatform(str.platform)
|
|
37
|
+
const profile = selectProfile(str.d, str.p, str.live, str.profile)
|
|
38
|
+
const isDev = profile === 'development'
|
|
39
|
+
//console.log(options);
|
|
40
|
+
console.log(`Building ${platform} with profile ${'`'}${profile}${'`'}`)
|
|
41
|
+
console.log('\n\n\n')
|
|
42
|
+
|
|
43
|
+
const EXPO_FORCE_PRODUCTION = str.force || isDev ? 'false' : 'true'
|
|
44
|
+
console.log('EXPO_FORCE_PRODUCTION', EXPO_FORCE_PRODUCTION)
|
|
45
|
+
|
|
46
|
+
let envObj = generateEnvValues(EXPO_FORCE_PRODUCTION, '', false)
|
|
47
|
+
if (!isDev) {
|
|
48
|
+
envObj.EXPO_PUBLIC_API_URL = `${STAGING_URL}`
|
|
49
|
+
envObj.EXPO_FORCE_PROD = 'true'
|
|
50
|
+
} else {
|
|
51
|
+
const publicUrl = process.env.EXPO_PUBLIC_API_URL || `${STAGING_URL}`
|
|
52
|
+
envObj.EXPO_PUBLIC_API_URL = publicUrl
|
|
53
|
+
}
|
|
54
|
+
console.log('envObj', envObj)
|
|
55
|
+
envObj.EAS_BUILD_PROFILE = profile // your custom variable
|
|
56
|
+
|
|
57
|
+
const command = `w expo-app eas build --profile ${profile} --platform ${platform}`
|
|
58
|
+
console.log('Running command:', command)
|
|
59
|
+
const child = spawn('yarn', [command], {
|
|
60
|
+
stdio: 'inherit',
|
|
61
|
+
shell: true, // required if using shell-style commands or cross-platform support
|
|
62
|
+
env: {
|
|
63
|
+
...envObj,
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
child.on('exit', (code) => {
|
|
68
|
+
console.log(`Process exited with code ${code}`)
|
|
69
|
+
process.exit(code ?? 0)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import { spawn } from 'child_process'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import { program } from '../../app.js'
|
|
4
|
+
import { generateEnvValues } from '../../app.js'
|
|
5
|
+
import { STAGING_URL } from '../../config.js'
|
|
6
|
+
import { getHasteConfig, getHasteFiles, getRootDirectory, getRootJson } from '../loadFromRoot.js'
|
|
7
|
+
import { config } from 'process'
|
|
8
|
+
|
|
9
|
+
console.log('getRootDirectory', getRootDirectory())
|
|
10
|
+
console.log('rootJson', getRootJson())
|
|
11
|
+
const files = getHasteConfig()
|
|
12
|
+
|
|
13
|
+
console.log('Haste files', files)
|
|
14
|
+
|
|
15
|
+
const fileKeys = Object.keys(files)
|
|
16
|
+
let dataLayer = {}
|
|
17
|
+
|
|
18
|
+
fileKeys.forEach((key) => {
|
|
19
|
+
const commandName = key
|
|
20
|
+
|
|
21
|
+
console.log('Haste file key', commandName, files[key])
|
|
22
|
+
const configObject = files[commandName] || {}
|
|
23
|
+
const optionsData = configObject.options || {}
|
|
24
|
+
const optionsList = Object.keys(optionsData)
|
|
25
|
+
|
|
26
|
+
console.log('optionsList', optionsList, optionsData)
|
|
27
|
+
|
|
28
|
+
let currentCommand = program.command(commandName).description('Execute eas build command')
|
|
29
|
+
|
|
30
|
+
if (configObject.argument) {
|
|
31
|
+
console.log('has argument', configObject.argument)
|
|
32
|
+
// .argument('<input>', 'path to input file') // required
|
|
33
|
+
// .argument('[output]', 'optional output file') // optional
|
|
34
|
+
const argumentRequired = configObject.argument.required || false
|
|
35
|
+
const argCommand = argumentRequired
|
|
36
|
+
? `<${configObject.argument.type || 'string'}>`
|
|
37
|
+
: `[${configObject.argument.type || 'string'}]`
|
|
38
|
+
currentCommand = currentCommand.argument(argCommand, configObject.argument.description || '')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
optionsList.forEach((optionKey) => {
|
|
42
|
+
const type = optionsData[optionKey].type || 'boolean'
|
|
43
|
+
|
|
44
|
+
if (type === 'string') {
|
|
45
|
+
if (optionsData[optionKey].shortcut) {
|
|
46
|
+
currentCommand = currentCommand.option(
|
|
47
|
+
`-${optionsData[optionKey].shortcut}, --${optionKey} <${optionKey}>`,
|
|
48
|
+
optionsData[optionKey].description || ''
|
|
49
|
+
)
|
|
50
|
+
console.log('optionKey', optionKey, optionsData[optionKey])
|
|
51
|
+
dataLayer[optionKey] = optionsData[optionKey].default || ''
|
|
52
|
+
} else {
|
|
53
|
+
currentCommand = currentCommand.option(
|
|
54
|
+
`--${optionKey} <${optionKey}>`,
|
|
55
|
+
optionsData[optionKey].description || ''
|
|
56
|
+
)
|
|
57
|
+
dataLayer[optionKey] = optionsData[optionKey].default || ''
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
if (optionsData[optionKey].shortcut) {
|
|
61
|
+
currentCommand = currentCommand.option(
|
|
62
|
+
`-${optionsData[optionKey].shortcut}, --${optionKey}`,
|
|
63
|
+
optionsData[optionKey].description || ''
|
|
64
|
+
)
|
|
65
|
+
console.log('optionKey', optionKey, optionsData[optionKey])
|
|
66
|
+
dataLayer[optionKey] = optionsData[optionKey].default || false
|
|
67
|
+
} else {
|
|
68
|
+
currentCommand = currentCommand.option(
|
|
69
|
+
`--${optionKey}`,
|
|
70
|
+
optionsData[optionKey].description || ''
|
|
71
|
+
)
|
|
72
|
+
dataLayer[optionKey] = optionsData[optionKey].default || false
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
function verifyOptionValues(optionKey, value) {
|
|
78
|
+
const optionInfo = optionsData[optionKey]
|
|
79
|
+
if (optionInfo && optionInfo.options) {
|
|
80
|
+
if (!optionInfo.options.includes(value)) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Invalid value for --${optionKey}: ${value}. Valid options are: ${optionInfo.options.join(
|
|
83
|
+
', '
|
|
84
|
+
)}`
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return value
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
currentCommand.action(async (arg, cmd) => {
|
|
92
|
+
console.log('test:')
|
|
93
|
+
console.log('cmd', cmd)
|
|
94
|
+
console.log('arg', arg)
|
|
95
|
+
const optionVals = cmd.opts ? cmd.opts() : cmd
|
|
96
|
+
console.log('arg', arg)
|
|
97
|
+
console.log('optionVals')
|
|
98
|
+
|
|
99
|
+
const opts = optionVals
|
|
100
|
+
Object.keys(opts).forEach((k) => {
|
|
101
|
+
opts[k] = verifyOptionValues(k, opts[k])
|
|
102
|
+
})
|
|
103
|
+
const argVal = arg ? arg : configObject.argument.default
|
|
104
|
+
console.log('argVal', argVal)
|
|
105
|
+
dataLayer = { ...dataLayer, ...opts, arg: argVal }
|
|
106
|
+
console.log('opts', opts)
|
|
107
|
+
console.log('firstDataLayer', dataLayer)
|
|
108
|
+
await runHasteCommand(configObject, opts)
|
|
109
|
+
|
|
110
|
+
// const devConfig = configObject.environments ? configObject.environments.dev : {}
|
|
111
|
+
// const productionConfig = configObject.environments ? configObject.environments.production : {}
|
|
112
|
+
// let envObj = {}
|
|
113
|
+
// if (options.stage) {
|
|
114
|
+
// envObj = { ...devConfig }
|
|
115
|
+
// } else {
|
|
116
|
+
// envObj = { ...productionConfig }
|
|
117
|
+
// }
|
|
118
|
+
|
|
119
|
+
// // You can decide what happens when each flag is passed.
|
|
120
|
+
// // Example: run `eas build` with different profiles.
|
|
121
|
+
// if (options.android) {
|
|
122
|
+
// console.log('→ Building Android (preview profile)…')
|
|
123
|
+
// //run('eas', ['build', '--platform', 'android', '--profile', 'preview'])
|
|
124
|
+
// } else if (options.ios) {
|
|
125
|
+
// console.log('→ Building iOS (production profile)…')
|
|
126
|
+
// //run('eas', ['build', '--platform', 'ios', '--profile', 'production'])
|
|
127
|
+
// } else {
|
|
128
|
+
// // console.log('No target specified. Use --android or --ios.')
|
|
129
|
+
// // program.help()
|
|
130
|
+
// console.log('objHaste.actions', configObject)
|
|
131
|
+
// const preactions = configObject['preactions'] || []
|
|
132
|
+
// const actions = configObject.actions || []
|
|
133
|
+
// console.log('preactions', preactions)
|
|
134
|
+
// for (const item of preactions) {
|
|
135
|
+
// console.log(`→ Running pre-action: ${item}`)
|
|
136
|
+
// await run(item, [], envObj)
|
|
137
|
+
// }
|
|
138
|
+
// for (const item of actions) {
|
|
139
|
+
// console.log(`→ Running action: ${item}`)
|
|
140
|
+
// run(item, [], envObj)
|
|
141
|
+
// }
|
|
142
|
+
// }
|
|
143
|
+
// })
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// const commandName = 'test'
|
|
148
|
+
// program
|
|
149
|
+
// .command(commandName)
|
|
150
|
+
// .description('Execute eas build command')
|
|
151
|
+
// .option('--android', 'Build to target preview profile')
|
|
152
|
+
// .option('--ios', 'Build to target production profile')
|
|
153
|
+
// .option('--stage', 'Set environment to staging')
|
|
154
|
+
// .action((options) => {
|
|
155
|
+
// const configObject = objHaste[commandName] || {}
|
|
156
|
+
|
|
157
|
+
// const devConfig = configObject.environments ? configObject.environments.dev : {}
|
|
158
|
+
// const productionConfig = configObject.environments ? configObject.environments.production : {}
|
|
159
|
+
// let envObj = {}
|
|
160
|
+
// if (options.stage) {
|
|
161
|
+
// envObj = { ...devConfig }
|
|
162
|
+
// } else {
|
|
163
|
+
// envObj = { ...productionConfig }
|
|
164
|
+
// }
|
|
165
|
+
|
|
166
|
+
// // You can decide what happens when each flag is passed.
|
|
167
|
+
// // Example: run `eas build` with different profiles.
|
|
168
|
+
// if (options.android) {
|
|
169
|
+
// console.log('→ Building Android (preview profile)…')
|
|
170
|
+
// //run('eas', ['build', '--platform', 'android', '--profile', 'preview'])
|
|
171
|
+
// } else if (options.ios) {
|
|
172
|
+
// console.log('→ Building iOS (production profile)…')
|
|
173
|
+
// //run('eas', ['build', '--platform', 'ios', '--profile', 'production'])
|
|
174
|
+
// } else {
|
|
175
|
+
// // console.log('No target specified. Use --android or --ios.')
|
|
176
|
+
// // program.help()
|
|
177
|
+
// console.log('objHaste.actions', objHaste)
|
|
178
|
+
// for (const item of configObject.actions) {
|
|
179
|
+
// console.log(`→ Running action: ${item}`)
|
|
180
|
+
// run(item, [], envObj)
|
|
181
|
+
// }
|
|
182
|
+
// }
|
|
183
|
+
// })
|
|
184
|
+
|
|
185
|
+
const totalClosedActions = 0
|
|
186
|
+
// Utility to spawn and pipe child output
|
|
187
|
+
async function run(cmd, args, envObj = {}, count = 1) {
|
|
188
|
+
const child = spawn(cmd, args, {
|
|
189
|
+
stdio: 'inherit',
|
|
190
|
+
shell: true,
|
|
191
|
+
env: {
|
|
192
|
+
...process.env,
|
|
193
|
+
...envObj,
|
|
194
|
+
},
|
|
195
|
+
})
|
|
196
|
+
const isLast = count === totalClosedActions + 1
|
|
197
|
+
|
|
198
|
+
const exitAction = () => {
|
|
199
|
+
totalClosedActions++
|
|
200
|
+
process.exit(code)
|
|
201
|
+
}
|
|
202
|
+
if (!isLast) {
|
|
203
|
+
child.on('exit', (code) => {
|
|
204
|
+
exitAction()
|
|
205
|
+
})
|
|
206
|
+
child.on('sigint', () => {
|
|
207
|
+
exitAction()
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
if (isLast) {
|
|
211
|
+
child.on('exit', (code) => {
|
|
212
|
+
if (count < totalClosedActions) exitAction()
|
|
213
|
+
})
|
|
214
|
+
child.on('sigint', () => {
|
|
215
|
+
if (count < totalClosedActions) {
|
|
216
|
+
exitAction()
|
|
217
|
+
process.exit(code)
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Track background processes so we can kill them on exit
|
|
224
|
+
const bgChildren = new Set()
|
|
225
|
+
|
|
226
|
+
/** Run a command and resolve when it exits (attach stdio so you can see output). */
|
|
227
|
+
function runForeground(cmd, envObj = {}, options = {}) {
|
|
228
|
+
return new Promise((resolve, reject) => {
|
|
229
|
+
let lastLine = ''
|
|
230
|
+
let myTextData = ''
|
|
231
|
+
const child = spawn(cmd, {
|
|
232
|
+
shell: true,
|
|
233
|
+
env: { ...process.env, ...envObj },
|
|
234
|
+
stdio: ['inherit', 'pipe', 'pipe'], // stdin pass-through, capture out/err
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
const handleData = (chunk, isErr = false) => {
|
|
238
|
+
const text = chunk.toString()
|
|
239
|
+
console.log(text.toString().trim())
|
|
240
|
+
console.log('text', text)
|
|
241
|
+
myTextData = myTextData.concat(text)
|
|
242
|
+
console.log('myTextData', myTextData)
|
|
243
|
+
|
|
244
|
+
// Track last line
|
|
245
|
+
const lines = text.trim().split(/\r?\n/)
|
|
246
|
+
if (lines.length) {
|
|
247
|
+
lastLine = lines[lines.length - 1]
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
child.stdout.on('data', (chunk) => handleData(chunk, false))
|
|
252
|
+
child.stderr.on('data', (chunk) => handleData(chunk, true))
|
|
253
|
+
|
|
254
|
+
child.on('error', reject)
|
|
255
|
+
child.on('exit', (code, signal) => {
|
|
256
|
+
myTextData.concat('\n<<< CHILD PROCESS EXITED >>>\n')
|
|
257
|
+
console.log('myTextData', myTextData)
|
|
258
|
+
//const rx = /\{out\s*:\s*(.+?)\}/gs
|
|
259
|
+
// ^\{out:field-name \$\{([^}]+)\}\}$
|
|
260
|
+
const rx = /\{out:([^\s]+) (.*)\}\n/g
|
|
261
|
+
//const rx = /Project ([^\r\n]*)/
|
|
262
|
+
console.log('myTextData', myTextData)
|
|
263
|
+
const match = rx.exec(myTextData)
|
|
264
|
+
|
|
265
|
+
const rx3 = /\{out:(?<field>[^\s}]+)\s+(?<value>[^\s}]+)\}/g
|
|
266
|
+
const results = [...myTextData.matchAll(rx3)].map((m) => {
|
|
267
|
+
const layerIndex = m.groups.field
|
|
268
|
+
const matchValue = m.groups.value
|
|
269
|
+
console.log('matchValue', matchValue)
|
|
270
|
+
dataLayer[layerIndex] = matchValue
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
field: m.groups.field,
|
|
274
|
+
value: m.groups.value,
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
console.log(results)
|
|
278
|
+
|
|
279
|
+
console.log('match', match)
|
|
280
|
+
|
|
281
|
+
if (signal) return reject(new Error(`${cmd} exited via signal ${signal}`))
|
|
282
|
+
if (code === 0) return resolve(lastLine) // resolve with last line
|
|
283
|
+
reject(new Error(`${cmd} exited with code ${code}. Last line: ${lastLine}`))
|
|
284
|
+
})
|
|
285
|
+
})
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function runBackground(cmd, envObj = {}, options = {}, attached = false) {
|
|
289
|
+
const isWin = process.platform === 'win32'
|
|
290
|
+
// Expand ${0}, ${1}, ... from dataLayer into env strings
|
|
291
|
+
const newEnv = {}
|
|
292
|
+
const commandClone = cmd
|
|
293
|
+
for (const key of Object.keys(envObj)) {
|
|
294
|
+
let value = envObj[key]
|
|
295
|
+
if (typeof value === 'string') {
|
|
296
|
+
console.log('dataLayer', dataLayer)
|
|
297
|
+
Object.keys(dataLayer).map((k) => {
|
|
298
|
+
value = value.replace(new RegExp(`\\$\\{${k}\\}`, 'g'), dataLayer[k])
|
|
299
|
+
})
|
|
300
|
+
}
|
|
301
|
+
newEnv[key] = value
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
console.log('newEnv', newEnv)
|
|
305
|
+
|
|
306
|
+
const out = commandClone.replace(/\$\{([^}]+)\}/g, (match, key) => {
|
|
307
|
+
// keep 0/false, only treat null/undefined as missing
|
|
308
|
+
const v = dataLayer[key]
|
|
309
|
+
return v == null ? match : String(v)
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
return new Promise((resolve, reject) => {
|
|
313
|
+
const child = spawn(out, {
|
|
314
|
+
shell: true,
|
|
315
|
+
stdio: attached ? 'inherit' : 'ignore', // no output in this terminal
|
|
316
|
+
env: { ...process.env, ...newEnv },
|
|
317
|
+
// On POSIX, detach so we can signal the whole group; on Windows, DON'T.
|
|
318
|
+
detached: !attached && !isWin,
|
|
319
|
+
windowsHide: !attached && !isWin, // prevent new console window on Windows
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
// Only unref on POSIX when detached; on Windows keep it referenced.
|
|
323
|
+
if (!attached && !isWin) child.unref()
|
|
324
|
+
|
|
325
|
+
child.once('error', (err) => {
|
|
326
|
+
bgChildren.delete(child)
|
|
327
|
+
reject(err)
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
child.once('exit', (code, signal) => {
|
|
331
|
+
bgChildren.delete(child)
|
|
332
|
+
if (signal) return reject(new Error(`${cmd} exited via signal ${signal}`))
|
|
333
|
+
if (code === 0) return resolve()
|
|
334
|
+
reject(new Error(`${cmd} exited with code ${code}`))
|
|
335
|
+
})
|
|
336
|
+
})
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Kill all background children (called on SIGINT/SIGTERM or when foreground ends). */
|
|
340
|
+
function killAllBackground() {
|
|
341
|
+
for (const child of Array.from(bgChildren)) {
|
|
342
|
+
try {
|
|
343
|
+
if (process.platform === 'win32') {
|
|
344
|
+
spawn('taskkill', ['/PID', String(child.pid), '/T', '/F'], { shell: true, stdio: 'ignore' })
|
|
345
|
+
} else {
|
|
346
|
+
process.kill(-child.pid, 'SIGTERM') // whole group
|
|
347
|
+
}
|
|
348
|
+
} catch {}
|
|
349
|
+
}
|
|
350
|
+
bgChildren.clear()
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
process.on('SIGINT', () => {
|
|
354
|
+
console.log('\nSIGINT')
|
|
355
|
+
killAllBackground()
|
|
356
|
+
process.exit(130)
|
|
357
|
+
})
|
|
358
|
+
process.on('SIGTERM', () => {
|
|
359
|
+
killAllBackground()
|
|
360
|
+
process.exit(143)
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
/** In your commander .action handler */
|
|
364
|
+
async function runHasteCommand(configObject, options = {}) {
|
|
365
|
+
const environments = configObject.environments
|
|
366
|
+
const devConfig = configObject.environments?.dev ?? {}
|
|
367
|
+
const productionConfig = configObject.environments?.stage ?? {}
|
|
368
|
+
console.log('options', options)
|
|
369
|
+
const awsProfile = process.env.CDK_DEPLOY_PROFILE || 'default'
|
|
370
|
+
console.log('prof', awsProfile)
|
|
371
|
+
const envObj = options.stage ? productionConfig : devConfig
|
|
372
|
+
envObj['AWS_PROFILE'] = awsProfile
|
|
373
|
+
|
|
374
|
+
const preactions = configObject.preactions ?? []
|
|
375
|
+
const actions = configObject.actions ?? []
|
|
376
|
+
|
|
377
|
+
console.log('environments', environments)
|
|
378
|
+
|
|
379
|
+
console.log('preactions', preactions)
|
|
380
|
+
|
|
381
|
+
// 1) Run preactions SEQUENTIALLY (each waits for previous to finish)
|
|
382
|
+
let num = 0
|
|
383
|
+
console.log('envObj', envObj)
|
|
384
|
+
for (const cmd of preactions) {
|
|
385
|
+
console.log(`→ preaction: ${cmd}`)
|
|
386
|
+
await runForeground(cmd, envObj, options)
|
|
387
|
+
num++
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// 2) Run actions: background all but the last; attach to the last
|
|
391
|
+
if (actions.length === 0) return
|
|
392
|
+
|
|
393
|
+
const bg = actions.slice(0, -1)
|
|
394
|
+
const fg = actions[actions.length - 1]
|
|
395
|
+
|
|
396
|
+
for (const cmd of bg) {
|
|
397
|
+
console.log(`→ background action: ${cmd}`)
|
|
398
|
+
runBackground(cmd, envObj, options)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
console.log(`→ foreground action (attached): ${fg}`)
|
|
402
|
+
try {
|
|
403
|
+
console.log('envObj', envObj)
|
|
404
|
+
await runBackground(fg, envObj, options, true)
|
|
405
|
+
} finally {
|
|
406
|
+
// When the foreground ends, clean up background processes too (optional)
|
|
407
|
+
killAllBackground()
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
//ACTION TYPES: preaction, action
|
|
412
|
+
//COMMAND TYPES: sequence, parallel
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import { spawn } from 'child_process'
|
|
3
|
+
import { readFileSync } from 'fs'
|
|
4
|
+
|
|
5
|
+
import { program } from '../../app.js'
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
const packageJSON = JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf8'));
|
|
8
|
+
|
|
9
|
+
console.log('Deploy command loaded')
|
|
10
|
+
|
|
11
|
+
const awsObject = packageJSON['aws'] || {}
|
|
12
|
+
|
|
13
|
+
// const awsProfile = awsObject['profile'] || 'default'
|
|
14
|
+
const accountId = process.env.CDK_DEPLOY_ACCOUNT
|
|
15
|
+
const awsProfile = process.env.CDK_DEPLOY_PROFILE || 'default'
|
|
16
|
+
|
|
17
|
+
console.log('process.env', process.env)
|
|
18
|
+
console.log('CDK_DEPLOY_ACCOUNT', process.env.CDK_DEPLOY_PROFILE)
|
|
19
|
+
program
|
|
20
|
+
.command('deploy2')
|
|
21
|
+
.description('Execute cdk deploy command')
|
|
22
|
+
.argument('[<string>]', 'Environment to deploy')
|
|
23
|
+
.option('-d, --dev', 'Deploy to dev environment')
|
|
24
|
+
.option('-r, --region <region>', 'Region to deploy to')
|
|
25
|
+
.action((str, options) => {
|
|
26
|
+
const owner = str || 'dev'
|
|
27
|
+
const region = options.region || 'us-east-2'
|
|
28
|
+
console.log(`Deploying to ${owner} environment`)
|
|
29
|
+
const command = `workspace infra deploy`
|
|
30
|
+
const inputs = `-c owner=${owner} -c region=${region}`
|
|
31
|
+
console.log(`Inputs: ${inputs}`)
|
|
32
|
+
const child = spawn('yarn', [`${command} ${inputs}`], {
|
|
33
|
+
stdio: 'inherit',
|
|
34
|
+
shell: true, // required if using shell-style commands or cross-platform support
|
|
35
|
+
env: {
|
|
36
|
+
AWS_PROFILE: awsProfile,
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
child.on('exit', (code) => {
|
|
41
|
+
console.log(`Process exited with code ${code}`)
|
|
42
|
+
process.exit(code ?? 0)
|
|
43
|
+
})
|
|
44
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { spawn } from 'child_process'
|
|
2
|
+
|
|
3
|
+
import { program } from '../app.js'
|
|
4
|
+
|
|
5
|
+
program
|
|
6
|
+
.command('destroy')
|
|
7
|
+
.description('Destroys the current or specified cdk construct')
|
|
8
|
+
.argument('[<string>]', 'Environment to deploy')
|
|
9
|
+
.option('-d, --dev', 'Deploy to dev environment')
|
|
10
|
+
.option('-r, --region <region>', 'Region to deploy to')
|
|
11
|
+
.action((str, options) => {
|
|
12
|
+
const owner = str || 'dev'
|
|
13
|
+
const region = options.region || 'us-east-2'
|
|
14
|
+
console.log(`Deploying to ${owner} environment`)
|
|
15
|
+
const command = `workspace infra cdk destroy`
|
|
16
|
+
const inputs = `-c owner=${owner} -c region=${region}`
|
|
17
|
+
console.log(`Inputs: ${inputs}`)
|
|
18
|
+
const child = spawn('yarn', [`${command} ${inputs}`], {
|
|
19
|
+
stdio: 'inherit',
|
|
20
|
+
shell: true, // required if using shell-style commands or cross-platform support
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
child.on('exit', (code) => {
|
|
24
|
+
console.log(`Process exited with code ${code}`)
|
|
25
|
+
process.exit(code ?? 0)
|
|
26
|
+
})
|
|
27
|
+
})
|