@stacksjs/buddy 0.58.48 β 0.58.49
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/package.json +3 -2
- package/src/cli.ts +50 -0
- package/src/commands/add.ts +69 -0
- package/src/commands/build.ts +228 -0
- package/src/commands/changelog.ts +38 -0
- package/src/commands/clean.ts +36 -0
- package/src/commands/cloud.ts +305 -0
- package/src/commands/commit.ts +24 -0
- package/src/commands/configure.ts +68 -0
- package/src/commands/create.ts +131 -0
- package/src/commands/deploy.ts +100 -0
- package/src/commands/dev.ts +268 -0
- package/src/commands/dns.ts +80 -0
- package/src/commands/domains.ts +158 -0
- package/src/commands/example.ts +66 -0
- package/src/commands/fresh.ts +36 -0
- package/src/commands/generate.ts +140 -0
- package/src/commands/http.ts +55 -0
- package/src/commands/index.ts +30 -0
- package/src/commands/inspire.ts +24 -0
- package/src/commands/install.ts +28 -0
- package/src/commands/key.ts +34 -0
- package/src/commands/lint.ts +53 -0
- package/src/commands/make.ts +269 -0
- package/src/commands/migrate.ts +58 -0
- package/src/commands/prepublish.ts +25 -0
- package/src/commands/release.ts +35 -0
- package/src/commands/seed.ts +38 -0
- package/src/commands/setup.ts +115 -0
- package/src/commands/test.ts +124 -0
- package/src/commands/tinker.ts +36 -0
- package/src/commands/types.ts +33 -0
- package/src/commands/upgrade.ts +115 -0
- package/src/commands/version.ts +24 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
import { intro, italic, log, outro, prompts, runCommand, runCommandSync, underline } from '@stacksjs/cli'
|
|
3
|
+
import { addJumpBox, deleteCdkRemnants, deleteIamUsers, deleteJumpBox, deleteLogGroups, deleteParameterStore, deleteStacksBuckets, deleteStacksFunctions, getJumpBoxInstanceId } from '@stacksjs/cloud'
|
|
4
|
+
import { path as p } from '@stacksjs/path'
|
|
5
|
+
import type { CLI, CloudCliOptions } from '@stacksjs/types'
|
|
6
|
+
import { ExitCode } from '@stacksjs/types'
|
|
7
|
+
import { loop } from '@stacksjs/utils'
|
|
8
|
+
|
|
9
|
+
export function cloud(buddy: CLI) {
|
|
10
|
+
const descriptions = {
|
|
11
|
+
cloud: 'Interact with the Stacks Cloud',
|
|
12
|
+
ssh: 'SSH into the Stacks Cloud',
|
|
13
|
+
add: 'Add a resource to the Stacks Cloud',
|
|
14
|
+
remove: 'Removes the Stacks Cloud. In case it fails, try again',
|
|
15
|
+
optimizeCost: 'Removes certain resources that may be re-applied at a later time',
|
|
16
|
+
cleanUp: 'Removes all resources that were retained during the cloud deletion',
|
|
17
|
+
project: 'Target a specific project',
|
|
18
|
+
verbose: 'Enable verbose output',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
buddy
|
|
22
|
+
.command('cloud', descriptions.cloud)
|
|
23
|
+
.option('--ssh', descriptions.ssh, { default: false })
|
|
24
|
+
.option('--connect', descriptions.ssh, { default: false })
|
|
25
|
+
.option('-p, --project', descriptions.project, { default: false })
|
|
26
|
+
.option('--verbose', descriptions.verbose, { default: false })
|
|
27
|
+
.action(async (options: CloudCliOptions) => {
|
|
28
|
+
if (options.ssh || options.connect) {
|
|
29
|
+
const startTime = performance.now()
|
|
30
|
+
const jumpBoxId = await getJumpBoxInstanceId()
|
|
31
|
+
const result = await runCommand(`aws ssm start-session --target ${jumpBoxId}`, {
|
|
32
|
+
...options,
|
|
33
|
+
cwd: p.projectPath(),
|
|
34
|
+
stdin: 'pipe',
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
if (result.isErr()) {
|
|
38
|
+
await outro('While running the cloud command, there was an issue', { startTime, useSeconds: true }, result.error)
|
|
39
|
+
process.exit(ExitCode.FatalError)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await outro('Exited', { startTime, useSeconds: true })
|
|
43
|
+
process.exit(ExitCode.Success)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
log.info('Not implemented yet. Please use the --ssh (or --connect) flag to connect to the Stacks Cloud.')
|
|
47
|
+
process.exit(ExitCode.Success)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
buddy
|
|
51
|
+
.command('cloud:add', descriptions.add)
|
|
52
|
+
.option('--jump-box', 'Remove the jump-box', { default: false })
|
|
53
|
+
.option('-p, --project', descriptions.project, { default: false })
|
|
54
|
+
.option('--verbose', descriptions.verbose, { default: false })
|
|
55
|
+
.action(async (options: CloudCliOptions) => {
|
|
56
|
+
const startTime = await intro('buddy cloud:add')
|
|
57
|
+
|
|
58
|
+
if (options.jumpBox) {
|
|
59
|
+
const { confirm } = await prompts({
|
|
60
|
+
name: 'confirm',
|
|
61
|
+
type: 'confirm',
|
|
62
|
+
message: 'Would you like to add a jump-box to your cloud?',
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
if (!confirm) {
|
|
66
|
+
await outro('Exited', { startTime, useSeconds: true })
|
|
67
|
+
process.exit(ExitCode.Success)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
log.info('The jump-box is getting added to your cloud resources...')
|
|
71
|
+
log.info('This takes a few moments, please be patient.')
|
|
72
|
+
// sleep for 2 seconds to get the user to read the message
|
|
73
|
+
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
74
|
+
|
|
75
|
+
const result = await addJumpBox()
|
|
76
|
+
|
|
77
|
+
if (result.isErr()) {
|
|
78
|
+
await outro('While running the cloud:add command, there was an issue', { startTime, useSeconds: true }, result.error)
|
|
79
|
+
process.exit(ExitCode.FatalError)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
log.info(italic('View the jump-box in the AWS console:'))
|
|
83
|
+
log.info(underline('https://us-east-1.console.aws.amazon.com/ec2/home?region=us-east-1#Instances:instanceState=running'))
|
|
84
|
+
log.info(italic('Once it finished initializing, you may SSH into it:'))
|
|
85
|
+
log.info(underline(('buddy cloud --ssh')))
|
|
86
|
+
|
|
87
|
+
await outro('Your jump-box was added.', { startTime, useSeconds: true })
|
|
88
|
+
process.exit(ExitCode.Success)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
log.info('This functionality is not yet implemented.')
|
|
92
|
+
process.exit(ExitCode.Success)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
buddy
|
|
96
|
+
.command('cloud:remove', descriptions.remove)
|
|
97
|
+
.alias('cloud:destroy')
|
|
98
|
+
.alias('cloud:rm')
|
|
99
|
+
.alias('undeploy')
|
|
100
|
+
.option('--jump-box', 'Remove the jump-box', { default: false })
|
|
101
|
+
.option('-p, --project', descriptions.project, { default: false })
|
|
102
|
+
.option('--verbose', descriptions.verbose, { default: false })
|
|
103
|
+
.action(async (options: CloudCliOptions) => {
|
|
104
|
+
const startTime = await intro('buddy cloud:remove')
|
|
105
|
+
|
|
106
|
+
if (options.jumpBox) {
|
|
107
|
+
const { confirm } = await prompts({
|
|
108
|
+
name: 'confirm',
|
|
109
|
+
type: 'confirm',
|
|
110
|
+
message: 'Would you like to remove your jump-box for now?',
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
if (!confirm) {
|
|
114
|
+
await outro('Exited', { startTime, useSeconds: true })
|
|
115
|
+
process.exit(ExitCode.Success)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const result = await deleteJumpBox()
|
|
119
|
+
|
|
120
|
+
if (result.isErr()) {
|
|
121
|
+
await outro('While removing your jump-box, there was an issue', { startTime, useSeconds: true }, result.error)
|
|
122
|
+
process.exit(ExitCode.FatalError)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
await outro('Your jump-box was removed.', { startTime, useSeconds: true })
|
|
126
|
+
process.exit(ExitCode.Success)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// eslint-disable-next-line no-console
|
|
130
|
+
console.log(` ${italic('βΉοΈ Removing your cloud resources takes a while to complete.')}`)
|
|
131
|
+
// eslint-disable-next-line no-console
|
|
132
|
+
console.log(` ${italic('Please note, your backups will not yet be deleted. Though,')}`)
|
|
133
|
+
// eslint-disable-next-line no-console
|
|
134
|
+
console.log(` ${italic('Backups are scheduled to delete themselves in 30 days.')}`)
|
|
135
|
+
|
|
136
|
+
// sleep for 2 seconds to get the user to read the message
|
|
137
|
+
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
138
|
+
|
|
139
|
+
const result = await runCommand(`bunx cdk destroy`, {
|
|
140
|
+
...options,
|
|
141
|
+
cwd: p.cloudPath(),
|
|
142
|
+
stdin: 'inherit',
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
if (result.isErr()) {
|
|
146
|
+
await outro('While running the cloud:remove ("undeploy") command, there was an issue', { startTime, useSeconds: true }, result.error)
|
|
147
|
+
process.exit(ExitCode.FatalError)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
await runCommand('buddy cloud:cleanup', {
|
|
151
|
+
...options,
|
|
152
|
+
cwd: p.projectPath(),
|
|
153
|
+
stdin: 'inherit',
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// TODO: this should not be necessary but for some reason some buckets with versions aren't properly getting deleted
|
|
157
|
+
// and because of that, we simply run the command several times, because eventually the versions will be deleted
|
|
158
|
+
// and consequently the buckets will be deleted
|
|
159
|
+
// the reason we are using 7 as the number of times to run the command is because it's the most amount of times I have had to run it to get it to delete everything
|
|
160
|
+
try {
|
|
161
|
+
log.info('Finalizing the removal of your cloud resources.')
|
|
162
|
+
log.info('This will take a few moments...')
|
|
163
|
+
loop(7, () => {
|
|
164
|
+
runCommandSync('buddy cloud:cleanup', {
|
|
165
|
+
...options,
|
|
166
|
+
cwd: p.projectPath(),
|
|
167
|
+
stdout: 'ignore',
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
await outro('Your cloud has been removed', { startTime, useSeconds: true })
|
|
172
|
+
process.exit(ExitCode.Success)
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
await outro('While cleaning up the cloud, there was an issue', { startTime, useSeconds: true }, error as Error)
|
|
176
|
+
process.exit(ExitCode.FatalError)
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
buddy
|
|
181
|
+
.command('cloud:optimize-cost', descriptions.optimizeCost)
|
|
182
|
+
.option('--jump-box', 'Remove the jump-box', { default: true })
|
|
183
|
+
.option('-p, --project', descriptions.project, { default: false })
|
|
184
|
+
.option('--verbose', descriptions.verbose, { default: false })
|
|
185
|
+
.action(async (options: CloudCliOptions) => {
|
|
186
|
+
const startTime = await intro('buddy cloud:remove')
|
|
187
|
+
|
|
188
|
+
if (options.jumpBox) {
|
|
189
|
+
const { confirm } = await prompts({
|
|
190
|
+
name: 'confirm',
|
|
191
|
+
type: 'confirm',
|
|
192
|
+
message: 'Would you like to remove your jump-box to optimize your costs?',
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
if (!confirm) {
|
|
196
|
+
await outro('Exited', { startTime, useSeconds: true })
|
|
197
|
+
process.exit(ExitCode.Success)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
await deleteJumpBox()
|
|
201
|
+
|
|
202
|
+
await outro('Your jump-box was removed & cost optimizations are applied.', { startTime, useSeconds: true })
|
|
203
|
+
process.exit(ExitCode.Success)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
await outro('No cost optimization was applied', { startTime, useSeconds: true })
|
|
207
|
+
process.exit(ExitCode.Success)
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
buddy
|
|
211
|
+
.command('cloud:cleanup', descriptions.cleanUp)
|
|
212
|
+
.alias('cloud:clean-up')
|
|
213
|
+
.option('-p, --project', descriptions.project, { default: false })
|
|
214
|
+
.option('--verbose', descriptions.verbose, { default: false })
|
|
215
|
+
.action(async () => {
|
|
216
|
+
const startTime = await intro('buddy cloud:cleanup')
|
|
217
|
+
|
|
218
|
+
log.info(`Cleaning up your cloud resources will take a while to complete. Please be patient.`)
|
|
219
|
+
|
|
220
|
+
// sleep for 2 seconds to get the user to read the message
|
|
221
|
+
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
222
|
+
|
|
223
|
+
log.info('Removing any jump-boxes...')
|
|
224
|
+
const result = await deleteJumpBox()
|
|
225
|
+
|
|
226
|
+
if (result.isErr()) {
|
|
227
|
+
if (result.error !== 'Jump-box not found') {
|
|
228
|
+
await outro('While removing your jump-box, there was an issue', { startTime, useSeconds: true }, result.error)
|
|
229
|
+
process.exit(ExitCode.FatalError)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
log.info('Removing any retained S3 buckets...')
|
|
234
|
+
const result2 = await deleteStacksBuckets()
|
|
235
|
+
|
|
236
|
+
if (result2.isErr()) {
|
|
237
|
+
await outro('While deleting the retained S3 buckets, there was an issue', { startTime, useSeconds: true }, result2.error)
|
|
238
|
+
process.exit(ExitCode.FatalError)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
log.info('Removing any retained Lambda functions...')
|
|
242
|
+
const result3 = await deleteStacksFunctions()
|
|
243
|
+
|
|
244
|
+
if (result3.isErr()) {
|
|
245
|
+
if (result3.error !== 'No stacks functions found')
|
|
246
|
+
await outro('While deleting the Origin Request Lambda function, there was an issue', { startTime, useSeconds: true }, result3.error)
|
|
247
|
+
|
|
248
|
+
process.exit(ExitCode.FatalError)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
log.info(result3.value)
|
|
252
|
+
|
|
253
|
+
log.info('Removing any remaining Stacks logs...')
|
|
254
|
+
const result4 = await deleteLogGroups()
|
|
255
|
+
// TODO: investigate other regions for edge (cloudfront) logs
|
|
256
|
+
|
|
257
|
+
if (result4.isErr()) {
|
|
258
|
+
await outro('While deleting the Stacks log groups, there was an issue', { startTime, useSeconds: true }, result4.error)
|
|
259
|
+
process.exit(ExitCode.FatalError)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// log.info('Removing any Backup Vaults...')
|
|
263
|
+
// const result5 = await deleteBackupVaults()
|
|
264
|
+
|
|
265
|
+
// if (result5.isErr()) {
|
|
266
|
+
// await outro('While deleting the Backup Vaults, there was an issue', { startTime, useSeconds: true }, result5.error)
|
|
267
|
+
// process.exit(ExitCode.FatalError)
|
|
268
|
+
// }
|
|
269
|
+
|
|
270
|
+
log.info('Removing any stored parameters...')
|
|
271
|
+
const result7 = await deleteParameterStore()
|
|
272
|
+
|
|
273
|
+
if (result7.isErr()) {
|
|
274
|
+
await outro('While deleting the Stacks log groups, there was an issue', { startTime, useSeconds: true }, result7.error)
|
|
275
|
+
process.exit(ExitCode.FatalError)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
log.info('Removing any CDK remnants...')
|
|
279
|
+
const result6 = await deleteCdkRemnants()
|
|
280
|
+
|
|
281
|
+
if (result6.isErr()) {
|
|
282
|
+
await outro('While deleting the Stacks log groups, there was an issue', { startTime, useSeconds: true }, result6.error)
|
|
283
|
+
process.exit(ExitCode.FatalError)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
log.info('Removing any IAM users...')
|
|
287
|
+
const result8 = await deleteIamUsers()
|
|
288
|
+
|
|
289
|
+
if (result8.isErr()) {
|
|
290
|
+
await outro('While deleting the Stacks log groups, there was an issue', { startTime, useSeconds: true }, result8.error)
|
|
291
|
+
process.exit(ExitCode.FatalError)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// TODO: needs to delete all Backup Vaults
|
|
295
|
+
// TODO: needs to delete all KMS keys
|
|
296
|
+
|
|
297
|
+
await outro('AWS resources have been removed', { startTime, useSeconds: true })
|
|
298
|
+
process.exit(ExitCode.Success)
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
buddy.on('cloud:*', () => {
|
|
302
|
+
console.error('Invalid command: %s\nSee --help for a list of available commands.', buddy.args.join(' '))
|
|
303
|
+
process.exit(1)
|
|
304
|
+
})
|
|
305
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
import type { CLI, FreshOptions } from '@stacksjs/types'
|
|
3
|
+
import { runCommit } from '@stacksjs/actions'
|
|
4
|
+
|
|
5
|
+
export function commit(buddy: CLI) {
|
|
6
|
+
const descriptions = {
|
|
7
|
+
commit: 'Commit your stashed changes',
|
|
8
|
+
project: 'Target a specific project',
|
|
9
|
+
verbose: 'Enable verbose output',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
buddy
|
|
13
|
+
.command('commit', descriptions.commit)
|
|
14
|
+
.option('-p, --project', descriptions.project, { default: false })
|
|
15
|
+
.option('--verbose', descriptions.verbose, { default: false })
|
|
16
|
+
.action(async (options: FreshOptions) => {
|
|
17
|
+
await runCommit(options)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
buddy.on('commit:*', () => {
|
|
21
|
+
console.error('Invalid command: %s\nSee --help for a list of available commands.', buddy.args.join(' '))
|
|
22
|
+
process.exit(1)
|
|
23
|
+
})
|
|
24
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
import type { CLI, ConfigureOptions } from '@stacksjs/types'
|
|
3
|
+
import { log, outro, runCommand } from '@stacksjs/cli'
|
|
4
|
+
import { path as p } from '@stacksjs/path'
|
|
5
|
+
import { config } from '@stacksjs/config'
|
|
6
|
+
import { ExitCode } from '@stacksjs/types'
|
|
7
|
+
|
|
8
|
+
export function configure(buddy: CLI) {
|
|
9
|
+
const descriptions = {
|
|
10
|
+
configure: 'Configure options',
|
|
11
|
+
aws: 'Configure the AWS connection',
|
|
12
|
+
project: 'Target a specific project',
|
|
13
|
+
verbose: 'Enable verbose output',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
buddy
|
|
17
|
+
.command('configure', descriptions.configure)
|
|
18
|
+
.option('--aws', descriptions.aws, { default: false })
|
|
19
|
+
.option('-p, --project', descriptions.project, { default: false })
|
|
20
|
+
.option('--verbose', descriptions.verbose, { default: false })
|
|
21
|
+
.action(async (options?: ConfigureOptions) => {
|
|
22
|
+
if (options?.aws) {
|
|
23
|
+
const startTime = performance.now()
|
|
24
|
+
const result = await runCommand('aws configure', {
|
|
25
|
+
...options,
|
|
26
|
+
cwd: p.projectPath(),
|
|
27
|
+
stdin: 'inherit',
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
if (result.isErr()) {
|
|
31
|
+
await outro('While running the configure command, there was an issue', { startTime, useSeconds: true }, result.error)
|
|
32
|
+
process.exit(ExitCode.FatalError)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await outro('Exited', { startTime, useSeconds: true })
|
|
36
|
+
process.exit(ExitCode.Success)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
log.info('Not implemented yet. Please use the --aws flag to configure AWS.')
|
|
40
|
+
process.exit(ExitCode.Success)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
buddy
|
|
44
|
+
.command('configure:aws', descriptions.aws)
|
|
45
|
+
.option('-p, --project', descriptions.project, { default: false })
|
|
46
|
+
.option('--verbose', descriptions.verbose, { default: false })
|
|
47
|
+
.action(async (options?: ConfigureOptions) => {
|
|
48
|
+
const startTime = performance.now()
|
|
49
|
+
const result = await runCommand(`aws configure --profile ${config.app.url}`, {
|
|
50
|
+
...options,
|
|
51
|
+
cwd: p.cloudPath(),
|
|
52
|
+
stdin: 'inherit',
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
if (result.isErr()) {
|
|
56
|
+
await outro('While running the cloud command, there was an issue', { startTime, useSeconds: true }, result.error)
|
|
57
|
+
process.exit(ExitCode.FatalError)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
await outro('Exited', { startTime, useSeconds: true })
|
|
61
|
+
process.exit(ExitCode.Success)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
buddy.on('configure:*', () => {
|
|
65
|
+
console.error('Invalid command: %s\nSee --help for a list of available commands.', buddy.args.join(' '))
|
|
66
|
+
process.exit(ExitCode.FatalError)
|
|
67
|
+
})
|
|
68
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
import { ExitCode } from '@stacksjs/types'
|
|
3
|
+
import type { CLI, CreateOptions } from '@stacksjs/types'
|
|
4
|
+
import { bold, cyan, dim, intro, log, runCommand } from '@stacksjs/cli'
|
|
5
|
+
import { useOnline } from '@stacksjs/utils'
|
|
6
|
+
import { isFolder } from '@stacksjs/storage'
|
|
7
|
+
import { resolve } from '@stacksjs/path'
|
|
8
|
+
import { Action } from '@stacksjs/enums'
|
|
9
|
+
import { runAction } from '@stacksjs/actions'
|
|
10
|
+
|
|
11
|
+
export function create(buddy: CLI) {
|
|
12
|
+
const descriptions = {
|
|
13
|
+
command: 'Create a new Stacks project',
|
|
14
|
+
ui: 'Are you building a UI?',
|
|
15
|
+
components: 'Are you building UI components?',
|
|
16
|
+
webComponents: 'Automagically built optimized custom elements/web components?',
|
|
17
|
+
vue: 'Automagically built a Vue component library?',
|
|
18
|
+
views: 'How about views?',
|
|
19
|
+
functions: 'Are you developing functions/composables?',
|
|
20
|
+
api: 'Are you building an API?',
|
|
21
|
+
database: 'Do you need a database?',
|
|
22
|
+
project: 'Target a specific project',
|
|
23
|
+
verbose: 'Enable verbose output',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
buddy
|
|
27
|
+
.command('new <name>', descriptions.command)
|
|
28
|
+
.option('-u, --ui', descriptions.ui, { default: true }) // if no, disregard remainder of questions wrt UI
|
|
29
|
+
.option('-c, --components', descriptions.components, { default: true }) // if no, -v and -w would be false
|
|
30
|
+
.option('-w, --web-components', descriptions.webComponents, { default: true })
|
|
31
|
+
.option('-v, --vue', descriptions.vue, { default: true })
|
|
32
|
+
.option('-p, --views', descriptions.views, { default: true }) // i.e. `buddy dev`
|
|
33
|
+
.option('-f, --functions', descriptions.functions, { default: true }) // if no, API would be false
|
|
34
|
+
.option('-a, --api', descriptions.api, { default: true }) // APIs need an HTTP server & assumes functions is true
|
|
35
|
+
.option('-d, --database', descriptions.database, { default: true })
|
|
36
|
+
.option('-p, --project', descriptions.project, { default: false })
|
|
37
|
+
.option('--verbose', descriptions.verbose, { default: false })
|
|
38
|
+
// .option('--auth', 'Scaffold an authentication?', { default: true })
|
|
39
|
+
.action(async (options: CreateOptions) => {
|
|
40
|
+
const startTime = await intro('stacks new')
|
|
41
|
+
const name = options.name
|
|
42
|
+
const path = resolve(process.cwd(), name)
|
|
43
|
+
|
|
44
|
+
isFolderCheck(path)
|
|
45
|
+
onlineCheck()
|
|
46
|
+
const result = await download(name, path, options)
|
|
47
|
+
|
|
48
|
+
if (result?.isErr()) {
|
|
49
|
+
log.error(result.error)
|
|
50
|
+
process.exit(ExitCode.FatalError)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await ensureEnv(path, options)
|
|
54
|
+
await install(path, options)
|
|
55
|
+
|
|
56
|
+
if (startTime) {
|
|
57
|
+
const time = performance.now() - startTime
|
|
58
|
+
log.success(dim(`[${time.toFixed(2)}ms] Completed`))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
log.info(bold('Welcome to the Stacks Framework! βοΈ'))
|
|
62
|
+
log.info('To learn more, visit https://stacksjs.org')
|
|
63
|
+
|
|
64
|
+
process.exit(ExitCode.Success)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
buddy.on('new:*', () => {
|
|
68
|
+
console.error('Invalid command: %s\nSee --help for a list of available commands.', buddy.args.join(' '))
|
|
69
|
+
process.exit(1)
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isFolderCheck(path: string) {
|
|
74
|
+
if (isFolder(path)) {
|
|
75
|
+
log.error(`Path ${path} already exists`)
|
|
76
|
+
process.exit(ExitCode.FatalError)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function onlineCheck() {
|
|
81
|
+
const online = useOnline()
|
|
82
|
+
if (!online) {
|
|
83
|
+
log.info('It appears you are disconnected from the internet.')
|
|
84
|
+
log.info('The Stacks setup requires a brief internet connection for setup.')
|
|
85
|
+
log.info('For instance, it installs your dependencies from.')
|
|
86
|
+
process.exit(ExitCode.FatalError)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function download(name: string, path: string, options: CreateOptions) {
|
|
91
|
+
log.info('Setting up your stack.')
|
|
92
|
+
const result = await runCommand(`bunx giget stacks ${name}`, options)
|
|
93
|
+
log.success(`Successfully scaffolded your project at ${cyan(path)}`)
|
|
94
|
+
|
|
95
|
+
return result
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function ensureEnv(path: string, options: CreateOptions) {
|
|
99
|
+
log.info('Ensuring your environment is ready...')
|
|
100
|
+
await runCommand('pkgx --update ', { ...options, cwd: path })
|
|
101
|
+
log.success('Environment is ready')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function install(path: string, options: CreateOptions) {
|
|
105
|
+
log.info('Installing & setting up Stacks')
|
|
106
|
+
let result = await runCommand('bun install', { ...options, cwd: path })
|
|
107
|
+
|
|
108
|
+
if (result?.isErr()) {
|
|
109
|
+
log.error(result.error)
|
|
110
|
+
process.exit()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
result = await runCommand('cp .env.example .env', { ...options, cwd: path })
|
|
114
|
+
|
|
115
|
+
if (result?.isErr()) {
|
|
116
|
+
log.error(result.error)
|
|
117
|
+
process.exit(ExitCode.FatalError)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await runAction(Action.KeyGenerate, { ...options, cwd: path })
|
|
121
|
+
|
|
122
|
+
// TODO: we should ask quite a few questions here, similar how we do in `buddy new my-project`, so we can generate a custom pkgx.yaml file
|
|
123
|
+
|
|
124
|
+
result = await runCommand('git init', { ...options, cwd: path }) // do we need this? or does giget do this already?
|
|
125
|
+
if (result.isErr()) {
|
|
126
|
+
log.error(result.error)
|
|
127
|
+
process.exit(ExitCode.FatalError)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
log.success('Installed & set-up π')
|
|
131
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import process from 'node:process'
|
|
3
|
+
import { ExitCode } from '@stacksjs/types'
|
|
4
|
+
import type { CLI, DeployOptions } from '@stacksjs/types'
|
|
5
|
+
import { runAction } from '@stacksjs/actions'
|
|
6
|
+
import { intro, italic, log, outro } from '@stacksjs/cli'
|
|
7
|
+
import { Action } from '@stacksjs/enums'
|
|
8
|
+
import { app } from '@stacksjs/config'
|
|
9
|
+
import { addDomain, hasUserDomainBeenAddedToCloud } from '@stacksjs/dns'
|
|
10
|
+
|
|
11
|
+
export function deploy(buddy: CLI) {
|
|
12
|
+
const descriptions = {
|
|
13
|
+
deploy: 'Reinstalls your npm dependencies',
|
|
14
|
+
project: 'Target a specific project',
|
|
15
|
+
verbose: 'Enable verbose output',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
buddy
|
|
19
|
+
.command('deploy', descriptions.deploy)
|
|
20
|
+
.option('--domain', 'Specify a domain to deploy to', { default: undefined })
|
|
21
|
+
.option('-p, --project', descriptions.project, { default: false })
|
|
22
|
+
.option('--verbose', descriptions.verbose, { default: false })
|
|
23
|
+
.action(async (options: DeployOptions) => {
|
|
24
|
+
const startTime = await intro('buddy deploy')
|
|
25
|
+
const domain = options.domain || app.url
|
|
26
|
+
|
|
27
|
+
if (!domain) {
|
|
28
|
+
log.info('We could not identify a domain to deploy to.')
|
|
29
|
+
log.info('Please set your .env or ./config/app.ts properly.')
|
|
30
|
+
console.log('')
|
|
31
|
+
log.info('Alternatively, specify a domain to deploy via the `--domain` flag.')
|
|
32
|
+
console.log('')
|
|
33
|
+
log.info(' β‘οΈ Example: `buddy deploy --domain example.com`')
|
|
34
|
+
console.log('')
|
|
35
|
+
process.exit(ExitCode.FatalError)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// TODO: we can improve this check at some point, otherwise domains that legitimately include the word localhost will fail
|
|
39
|
+
if (domain.includes('localhost')) {
|
|
40
|
+
log.info('You are deploying to a local environment.')
|
|
41
|
+
log.info('Please set your .env or ./config/app.ts properly.')
|
|
42
|
+
console.log('')
|
|
43
|
+
log.info('Alternatively, specify a domain to deploy via the `--domain` flag.')
|
|
44
|
+
console.log('')
|
|
45
|
+
log.info(' β‘οΈ Example: `buddy deploy --domain example.com`')
|
|
46
|
+
console.log('')
|
|
47
|
+
process.exit(ExitCode.FatalError)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (await hasUserDomainBeenAddedToCloud(domain)) {
|
|
51
|
+
log.success('Your domain is properly configured.')
|
|
52
|
+
log.info('Your cloud is deploying...')
|
|
53
|
+
|
|
54
|
+
console.log(`${italic('This may take a while...')}`)
|
|
55
|
+
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
56
|
+
options.domain = domain
|
|
57
|
+
|
|
58
|
+
// now that we know the domain has been added to the users (AWS) cloud, we can deploy
|
|
59
|
+
const result = await runAction(Action.Deploy, options)
|
|
60
|
+
|
|
61
|
+
if (result.isErr()) {
|
|
62
|
+
await outro('While running the `buddy deploy`, there was an issue', { startTime, useSeconds: true }, result.error)
|
|
63
|
+
process.exit(ExitCode.FatalError)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
await outro('Deployment succeeded.', { startTime, useSeconds: true })
|
|
67
|
+
|
|
68
|
+
process.exit(ExitCode.Success)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// if the domain hasn't been added to the user's (AWS) cloud, we will add it for them
|
|
72
|
+
// and then exit the process with prompts for the user to update their nameservers
|
|
73
|
+
console.log('')
|
|
74
|
+
log.info(` π It appears to be your first ${italic(domain)} deployment.`)
|
|
75
|
+
console.log('')
|
|
76
|
+
log.info(italic('Letβs ensure it is all connected properly.'))
|
|
77
|
+
log.info(italic('One moment...'))
|
|
78
|
+
console.log('')
|
|
79
|
+
|
|
80
|
+
options.domain = domain
|
|
81
|
+
const result = await addDomain({
|
|
82
|
+
...options,
|
|
83
|
+
deploy: true,
|
|
84
|
+
startTime,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
if (result.isErr()) {
|
|
88
|
+
await outro('While running the `buddy deploy`, there was an issue', { startTime, useSeconds: true }, result.error)
|
|
89
|
+
process.exit(ExitCode.FatalError)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
await outro('Added your domain.', { startTime, useSeconds: true })
|
|
93
|
+
process.exit(ExitCode.Success)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
buddy.on('deploy:*', () => {
|
|
97
|
+
log.error('Invalid command: %s\nSee --help for a list of available commands.', buddy.args.join(' '))
|
|
98
|
+
process.exit(1)
|
|
99
|
+
})
|
|
100
|
+
}
|