create-platformatic 1.0.0 → 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,8 +1,9 @@
1
1
  import { writeFile, mkdir, appendFile } from 'fs/promises'
2
2
  import { join } from 'path'
3
- import { findDBConfigFile, isFileAccessible } from '../utils.mjs'
3
+ import { addPrefixToEnv, findDBConfigFile, isFileAccessible } from '../utils.mjs'
4
4
  import { getTsConfig } from '../get-tsconfig.mjs'
5
5
  import { generatePlugins } from '../create-plugins.mjs'
6
+ import { createDynamicWorkspaceGHAction, createStaticWorkspaceGHAction } from '../ghaction.mjs'
6
7
 
7
8
  const connectionStrings = {
8
9
  postgres: 'postgres://postgres:postgres@127.0.0.1:5432/postgres',
@@ -249,11 +250,12 @@ test('movies', async (t) => {
249
250
  })
250
251
  `
251
252
 
252
- function generateConfig (isRuntimeContext, migrations, plugin, types, typescript, version) {
253
+ function generateConfig (isRuntimeContext, migrations, plugin, types, typescript, version, envPrefix) {
254
+ const connectionStringValue = envPrefix ? `PLT_${envPrefix}DATABASE_URL` : 'DATABASE_URL'
253
255
  const config = {
254
256
  $schema: `https://platformatic.dev/schemas/v${version}/db`,
255
257
  db: {
256
- connectionString: '{DATABASE_URL}',
258
+ connectionString: `{${connectionStringValue}}`,
257
259
  graphql: true,
258
260
  openapi: true,
259
261
  schemalock: true
@@ -297,17 +299,18 @@ function generateConfig (isRuntimeContext, migrations, plugin, types, typescript
297
299
  }
298
300
 
299
301
  if (typescript === true) {
300
- config.plugins.typescript = '{PLT_TYPESCRIPT}'
302
+ config.plugins.typescript = `{PLT_${envPrefix}TYPESCRIPT}`
301
303
  }
302
304
 
303
305
  return config
304
306
  }
305
307
 
306
- function generateEnv (isRuntimeContext, hostname, port, connectionString, typescript) {
307
- let env = `\
308
- DATABASE_URL=${connectionString}
309
-
310
- `
308
+ function generateEnv (isRuntimeContext, hostname, port, connectionString, typescript, envPrefix) {
309
+ let env = ''
310
+ if (envPrefix) {
311
+ env += `PLT_${envPrefix}`
312
+ }
313
+ env += `DATABASE_URL=${connectionString}\n`
311
314
 
312
315
  if (!isRuntimeContext) {
313
316
  env += `\
@@ -322,8 +325,7 @@ PLT_SERVER_LOGGER_LEVEL=info
322
325
  env += `\
323
326
  # Set to false to disable automatic typescript compilation.
324
327
  # Changing this setting is needed for production
325
- PLT_TYPESCRIPT=true
326
-
328
+ PLT_${envPrefix}TYPESCRIPT=true
327
329
  `
328
330
  }
329
331
 
@@ -344,18 +346,35 @@ export async function createDB (params, logger, currentDir, version) {
344
346
  plugin = true,
345
347
  types = true,
346
348
  typescript = false,
347
- connectionString
349
+ connectionString,
350
+ staticWorkspaceGitHubAction,
351
+ dynamicWorkspaceGitHubAction,
352
+ runtimeContext
348
353
  } = params
349
354
 
355
+ const dbEnv = {
356
+ DATABASE_URL: connectionString,
357
+ PLT_SERVER_LOGGER_LEVEL: 'info',
358
+ PORT: port,
359
+ PLT_SERVER_HOSTNAME: hostname
360
+ }
361
+
362
+ if (typescript) {
363
+ dbEnv.PLT_TYPESCRIPT = true
364
+ }
350
365
  connectionString = connectionString || getConnectionString(database)
351
366
  const createMigrations = !!migrations // If we don't define a migrations folder, we don't create it
352
367
  const accessibleConfigFilename = await findDBConfigFile(currentDir)
368
+
353
369
  if (accessibleConfigFilename === undefined) {
354
- const config = generateConfig(isRuntimeContext, migrations, plugin, types, typescript, version)
370
+ const envPrefix = runtimeContext !== undefined ? `${runtimeContext.envPrefix}_` : ''
371
+
372
+ const config = generateConfig(isRuntimeContext, migrations, plugin, types, typescript, version, envPrefix)
355
373
  await writeFile(join(currentDir, 'platformatic.db.json'), JSON.stringify(config, null, 2))
356
374
  logger.info('Configuration file platformatic.db.json successfully created.')
357
- const env = generateEnv(isRuntimeContext, hostname, port, connectionString, typescript)
358
- const envSample = generateEnv(isRuntimeContext, hostname, port, getConnectionString(database), typescript)
375
+
376
+ const env = generateEnv(isRuntimeContext, hostname, port, connectionString, typescript, envPrefix)
377
+ const envSample = generateEnv(isRuntimeContext, hostname, port, getConnectionString(database), typescript, envPrefix)
359
378
  const envFileExists = await isFileAccessible('.env', currentDir)
360
379
  await appendFile(join(currentDir, '.env'), env)
361
380
  await writeFile(join(currentDir, '.env.sample'), envSample)
@@ -436,17 +455,17 @@ export async function createDB (params, logger, currentDir, version) {
436
455
  }
437
456
  }
438
457
 
439
- const output = {
440
- DATABASE_URL: connectionString,
441
- PLT_SERVER_LOGGER_LEVEL: 'info',
442
- PORT: port,
443
- PLT_SERVER_HOSTNAME: hostname
458
+ if (staticWorkspaceGitHubAction) {
459
+ await createStaticWorkspaceGHAction(logger, dbEnv, './platformatic.db.json', currentDir, typescript)
460
+ }
461
+ if (dynamicWorkspaceGitHubAction) {
462
+ await createDynamicWorkspaceGHAction(logger, dbEnv, './platformatic.db.json', currentDir, typescript)
444
463
  }
445
464
 
446
- if (typescript) {
447
- output.PLT_TYPESCRIPT = true
465
+ if (isRuntimeContext) {
466
+ return addPrefixToEnv(isRuntimeContext)
448
467
  }
449
- return output
468
+ return dbEnv
450
469
  }
451
470
 
452
471
  export default createDB
package/src/ghaction.mjs CHANGED
@@ -1,13 +1,12 @@
1
1
  import { join } from 'path'
2
- import inquirer from 'inquirer'
3
2
  import { isFileAccessible } from './utils.mjs'
4
3
  import { writeFile, mkdir } from 'fs/promises'
5
4
  import columnify from 'columnify'
6
5
  function envAsString (env, indent) {
7
6
  const spaces = Array(indent * 2).join(' ')
8
7
  return Object.keys(env).reduce((acc, key) => {
9
- if (key === 'DATABASE_URL') {
10
- acc += `${spaces}${key}: \${{ secrets.DATABASE_URL }}\n`
8
+ if (key.match('DATABASE_URL')) {
9
+ acc += `${spaces}${key}: \${{ secrets.${key} }}\n`
11
10
  } else {
12
11
  acc += `${spaces}${key}: ${env[key]} \n`
13
12
  }
@@ -131,10 +130,12 @@ export const createDynamicWorkspaceGHAction = async (logger, env, config, projec
131
130
  await mkdir(join(projectDir, '.github', 'workflows'), { recursive: true })
132
131
  await writeFile(ghActionFilePath, dynamicWorkspaceGHTemplate(env, config, buildTS))
133
132
  logger.info('PR Previews are enabled for your app and the Github action was successfully created, please add the following secrets as repository secrets: ')
133
+ const envToBeAdded = { ...env }
134
+ delete envToBeAdded.PORT
134
135
  const secretsString = formatSecretsToAdd({
135
136
  PLATFORMATIC_DYNAMIC_WORKSPACE_ID: 'your workspace id',
136
137
  PLATFORMATIC_DYNAMIC_WORKSPACE_API_KEY: 'your workspace API key',
137
- DATABASE_URL: env.DATABASE_URL
138
+ ...envToBeAdded
138
139
  })
139
140
  logger.info(`\n ${secretsString}`)
140
141
  const isGitDir = await isFileAccessible('.git', projectDir)
@@ -146,24 +147,6 @@ export const createDynamicWorkspaceGHAction = async (logger, env, config, projec
146
147
  }
147
148
  }
148
149
 
149
- /* c8 ignore next 21 */
150
- export const askDynamicWorkspaceCreateGHAction = async (logger, env, type, buildTS, projectDir = process.cwd()) => {
151
- const { githubAction } = await inquirer.prompt([
152
- {
153
- type: 'list',
154
- name: 'githubAction',
155
- message: 'Do you want to enable PR Previews in your application?',
156
- default: true,
157
- choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
158
- }
159
- ])
160
- if (githubAction) {
161
- const config = `./platformatic.${type}.json`
162
- await createDynamicWorkspaceGHAction(logger, env, config, projectDir, buildTS)
163
- }
164
- /* c8 ignore next */
165
- }
166
-
167
150
  export const createStaticWorkspaceGHAction = async (logger, env, config, projectDir, buildTS) => {
168
151
  const ghActionFileName = 'platformatic-static-workspace-deploy.yml'
169
152
  const ghActionFilePath = join(projectDir, '.github', 'workflows', ghActionFileName)
@@ -172,10 +155,12 @@ export const createStaticWorkspaceGHAction = async (logger, env, config, project
172
155
  await mkdir(join(projectDir, '.github', 'workflows'), { recursive: true })
173
156
  await writeFile(ghActionFilePath, staticWorkspaceGHTemplate(env, config, buildTS))
174
157
  logger.info('Github action successfully created, please add the following secrets as repository secrets: ')
158
+ const envToBeAdded = { ...env }
159
+ delete envToBeAdded.PORT
175
160
  const secretsString = formatSecretsToAdd({
176
161
  PLATFORMATIC_STATIC_WORKSPACE_ID: 'your workspace id',
177
162
  PLATFORMATIC_STATIC_WORKSPACE_API_KEY: 'your workspace API key',
178
- DATABASE_URL: env.DATABASE_URL
163
+ ...envToBeAdded
179
164
  })
180
165
  logger.info(`\n ${secretsString}`)
181
166
  const isGitDir = await isFileAccessible('.git', projectDir)
@@ -186,21 +171,3 @@ export const createStaticWorkspaceGHAction = async (logger, env, config, project
186
171
  logger.info(`Github action file ${ghActionFilePath} found, skipping creation of github action file.`)
187
172
  }
188
173
  }
189
-
190
- /* c8 ignore next 21 */
191
- export const askStaticWorkspaceGHAction = async (logger, env, type, buildTS, projectDir = process.cwd()) => {
192
- const { githubAction } = await inquirer.prompt([
193
- {
194
- type: 'list',
195
- name: 'githubAction',
196
- message: 'Do you want to create the github action to deploy this application to Platformatic Cloud?',
197
- default: true,
198
- choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
199
- }
200
- ])
201
- if (githubAction) {
202
- const config = `./platformatic.${type}.json`
203
- await createStaticWorkspaceGHAction(logger, env, config, projectDir, buildTS)
204
- }
205
- /* c8 ignore next */
206
- }
@@ -1,40 +1,20 @@
1
- import { getVersion, getDependencyVersion, isFileAccessible } from '../utils.mjs'
1
+ import { getVersion, getDependencyVersion, convertServiceNameToPrefix } from '../utils.mjs'
2
2
  import { createPackageJson } from '../create-package-json.mjs'
3
3
  import { createGitignore } from '../create-gitignore.mjs'
4
4
  import { getPkgManager } from '../get-pkg-manager.mjs'
5
- import { join, relative } from 'path'
5
+ import { join, relative, resolve } from 'path'
6
6
  import inquirer from 'inquirer'
7
- import { readFile, writeFile, mkdir } from 'fs/promises'
7
+ import { mkdir, stat } from 'fs/promises'
8
8
  import pino from 'pino'
9
9
  import pretty from 'pino-pretty'
10
10
  import { execa } from 'execa'
11
11
  import ora from 'ora'
12
12
  import createRuntime from './create-runtime.mjs'
13
13
  import askDir from '../ask-dir.mjs'
14
- import { askDynamicWorkspaceCreateGHAction, askStaticWorkspaceGHAction } from '../ghaction.mjs'
15
- import { getPort, getOverwriteReadme, getRunPackageManagerInstall } from '../cli-options.mjs'
14
+ import { getPort, getRunPackageManagerInstall } from '../cli-options.mjs'
16
15
  import generateName from 'boring-name-generator'
17
16
  import { chooseKind } from '../index.mjs'
18
-
19
- export const createReadme = async (logger, dir = '.') => {
20
- const readmeFileName = join(dir, 'README.md')
21
- let isReadmeExists = await isFileAccessible(readmeFileName)
22
- if (isReadmeExists) {
23
- logger.debug(`${readmeFileName} found, asking to overwrite it.`)
24
- const { shouldReplace } = await inquirer.prompt([getOverwriteReadme()])
25
- isReadmeExists = !shouldReplace
26
- }
27
-
28
- if (isReadmeExists) {
29
- logger.debug(`${readmeFileName} found, skipping creation of README.md file.`)
30
- return
31
- }
32
-
33
- const readmeFile = new URL('README.md', import.meta.url)
34
- const readme = await readFile(readmeFile, 'utf-8')
35
- await writeFile(readmeFileName, readme)
36
- logger.debug(`${readmeFileName} successfully created.`)
37
- }
17
+ import { createReadme } from '../create-readme.mjs'
38
18
 
39
19
  export async function createPlatformaticRuntime (_args) {
40
20
  const logger = pino(pretty({
@@ -45,17 +25,53 @@ export async function createPlatformaticRuntime (_args) {
45
25
  const version = await getVersion()
46
26
  const pkgManager = getPkgManager()
47
27
 
48
- const projectDir = await askDir(logger, '.')
28
+ const projectDir = await askDir(logger, join('.', 'platformatic-runtime'))
49
29
 
30
+ // checks directory
31
+ try {
32
+ await stat(projectDir)
33
+ logger.error(`Directory ${projectDir} already exists. Please choose another path.`)
34
+ process.exit(1)
35
+ } catch (err) {}
36
+ const toAsk = []
50
37
  // Create the project directory
51
38
  await mkdir(projectDir, { recursive: true })
52
39
 
53
40
  const baseServicesDir = join(relative(process.cwd(), projectDir), 'services')
54
41
  const servicesDir = await askDir(logger, baseServicesDir, 'Where would you like to load your services from?')
55
42
 
56
- const { runPackageManagerInstall } = await inquirer.prompt([
57
- getRunPackageManagerInstall(pkgManager)
58
- ])
43
+ // checks services dir is subdirectory
44
+ const resolvedDir = resolve(projectDir, servicesDir)
45
+ if (!resolvedDir.startsWith(projectDir)) {
46
+ logger.error(`Services directory must be a subdirectory of ${projectDir}. Found: ${resolvedDir}.`)
47
+ process.exit(1)
48
+ }
49
+
50
+ toAsk.push(getRunPackageManagerInstall(pkgManager))
51
+ // const { runPackageManagerInstall } = await inquirer.prompt([
52
+ // getRunPackageManagerInstall(pkgManager)
53
+ // ])
54
+
55
+ toAsk.push({
56
+ type: 'list',
57
+ name: 'staticWorkspaceGitHubAction',
58
+ message: 'Do you want to create the github action to deploy this application to Platformatic Cloud?',
59
+ default: true,
60
+ choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
61
+ },
62
+ {
63
+ type: 'list',
64
+ name: 'dynamicWorkspaceGitHubAction',
65
+ message: 'Do you want to enable PR Previews in your application?',
66
+ default: true,
67
+ choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
68
+ })
69
+
70
+ const {
71
+ runPackageManagerInstall,
72
+ staticWorkspaceGitHubAction,
73
+ dynamicWorkspaceGitHubAction
74
+ } = await inquirer.prompt(toAsk)
59
75
 
60
76
  await mkdir(servicesDir, { recursive: true })
61
77
 
@@ -65,13 +81,7 @@ export async function createPlatformaticRuntime (_args) {
65
81
  // the package.json with the TS build
66
82
  await createPackageJson(version, fastifyVersion, logger, projectDir, false)
67
83
  await createGitignore(logger, projectDir)
68
- await createReadme(logger, projectDir)
69
-
70
- if (runPackageManagerInstall) {
71
- const spinner = ora('Installing dependencies...').start()
72
- await execa(pkgManager, ['install'], { cwd: projectDir })
73
- spinner.succeed('...done!')
74
- }
84
+ await createReadme(logger, projectDir, 'runtime')
75
85
 
76
86
  logger.info('Let\'s create a first service!')
77
87
 
@@ -117,13 +127,18 @@ export async function createPlatformaticRuntime (_args) {
117
127
  const params = {
118
128
  servicesDir,
119
129
  entrypoint,
120
- entrypointPort
130
+ entrypointPort,
131
+ staticWorkspaceGitHubAction,
132
+ dynamicWorkspaceGitHubAction,
133
+ serviceNames: names
121
134
  }
122
135
 
123
- const env = await createRuntime(params, logger, projectDir, version)
124
-
125
- await askStaticWorkspaceGHAction(logger, env, 'service', false, projectDir)
126
- await askDynamicWorkspaceCreateGHAction(logger, env, 'service', false, projectDir)
136
+ await createRuntime(params, logger, projectDir, version)
137
+ if (runPackageManagerInstall) {
138
+ const spinner = ora('Installing dependencies...').start()
139
+ await execa(pkgManager, ['install'], { cwd: projectDir })
140
+ spinner.succeed()
141
+ }
127
142
  }
128
143
 
129
144
  export async function createRuntimeService ({ servicesDir, names, logger }) {
@@ -168,7 +183,8 @@ export async function createRuntimeService ({ servicesDir, names, logger }) {
168
183
  port: '0',
169
184
  isRuntimeContext: true,
170
185
  runtimeContext: {
171
- servicesNames: names
186
+ servicesNames: names,
187
+ envPrefix: convertServiceNameToPrefix(name)
172
188
  }
173
189
  })
174
190
 
@@ -1,7 +1,8 @@
1
- import { readFile, appendFile, writeFile } from 'fs/promises'
2
- import { findConfigFile, findRuntimeConfigFile } from '../utils.mjs'
1
+ import { readFile, writeFile, readdir, unlink } from 'fs/promises'
2
+ import { findRuntimeConfigFile } from '../utils.mjs'
3
3
  import { join, relative, isAbsolute } from 'path'
4
4
  import * as desm from 'desm'
5
+ import { createDynamicWorkspaceGHAction, createStaticWorkspaceGHAction } from '../ghaction.mjs'
5
6
 
6
7
  function generateConfig (version, path, entrypoint) {
7
8
  const config = {
@@ -19,7 +20,13 @@ function generateConfig (version, path, entrypoint) {
19
20
  }
20
21
 
21
22
  async function createRuntime (params, logger, currentDir = process.cwd(), version) {
22
- const { servicesDir, entrypoint, entrypointPort } = params
23
+ const {
24
+ servicesDir,
25
+ entrypoint,
26
+ entrypointPort,
27
+ staticWorkspaceGitHubAction,
28
+ dynamicWorkspaceGitHubAction
29
+ } = params
23
30
 
24
31
  if (!version) {
25
32
  const pkg = await readFile(desm.join(import.meta.url, '..', '..', 'package.json'))
@@ -35,28 +42,80 @@ async function createRuntime (params, logger, currentDir = process.cwd(), versio
35
42
  } else {
36
43
  logger.info(`Configuration file ${accessibleConfigFilename} found, skipping creation of configuration file.`)
37
44
  }
38
-
45
+ let runtimeEnv = {}
39
46
  if (servicesDir && entrypoint && entrypointPort) {
40
47
  const servicesDirFullPath = isAbsolute(servicesDir)
41
48
  ? servicesDir
42
49
  : join(currentDir, servicesDir)
43
50
 
44
- const entrypointPath = join(servicesDirFullPath, entrypoint)
45
- await updateEntrypointConfig(logger, entrypointPath)
46
- await updateEntrypointEnv(entrypointPort, logger, entrypointPath)
51
+ await updateServerConfig(currentDir)
52
+ runtimeEnv = await mergeEnvFiles(currentDir, servicesDirFullPath, entrypointPort)
53
+ }
54
+
55
+ if (staticWorkspaceGitHubAction) {
56
+ await createStaticWorkspaceGHAction(logger, runtimeEnv, './platformatic.runtime.json', currentDir, false)
57
+ }
58
+ if (dynamicWorkspaceGitHubAction) {
59
+ await createDynamicWorkspaceGHAction(logger, runtimeEnv, './platformatic.runtime.json', currentDir, false)
47
60
  }
48
61
 
49
62
  return {}
50
63
  }
64
+ /**
65
+ * @param {string} runtimeDir The runtime directory directory
66
+ * @param {string} servicesDir The services directory
67
+ * @returns {Promise} the global env object
68
+ */
69
+ async function mergeEnvFiles (runtimeDir, servicesDir, port) {
70
+ const dirs = await readdir(servicesDir)
71
+ let globalEnvContents = `# This file was generated by Platformatic
51
72
 
52
- async function updateEntrypointConfig (logger, currentDir) {
53
- const accessibleConfigFilename = await findConfigFile(currentDir)
54
- if (accessibleConfigFilename === undefined) {
55
- logger.error('Cannot find an entrypoint configuration file.')
56
- return
73
+ PORT=${port}
74
+ PLT_SERVER_HOSTNAME=127.0.0.1
75
+ PLT_SERVER_LOGGER_LEVEL=info
76
+ `
77
+ for (const dir of dirs) {
78
+ const envFilePath = join(servicesDir, dir, '.env')
79
+ const envFile = await readFile(envFilePath, 'utf8')
80
+ globalEnvContents += `${envFile}\n`
81
+ await unlink(envFilePath)
57
82
  }
58
83
 
59
- const configPath = join(currentDir, accessibleConfigFilename)
84
+ await writeFile(join(runtimeDir, '.env'), globalEnvContents)
85
+ await writeFile(join(runtimeDir, '.env.sample'), stripEnvFileValues(globalEnvContents))
86
+ return envStringToObject(globalEnvContents)
87
+ }
88
+
89
+ function stripEnvFileValues (envString) {
90
+ const lines = envString.split('\n')
91
+ const output = []
92
+ for (const line of lines) {
93
+ const match = line.match(/^(.*)=(.*)/)
94
+ if (match) {
95
+ const key = match[1]
96
+ output.push(`${key}=`)
97
+ } else {
98
+ output.push(line)
99
+ }
100
+ }
101
+ return output.join('\n')
102
+ }
103
+ function envStringToObject (envString) {
104
+ const lines = envString.split('\n')
105
+ const output = {}
106
+ for (const line of lines) {
107
+ const match = line.match(/^(.*)=(.*)/)
108
+ if (match) {
109
+ const key = match[1]
110
+ const value = match[2]
111
+ output[key] = value
112
+ }
113
+ }
114
+ return output
115
+ }
116
+
117
+ async function updateServerConfig (currentDir) {
118
+ const configPath = join(currentDir, 'platformatic.runtime.json')
60
119
  const config = JSON.parse(await readFile(configPath, 'utf8'))
61
120
 
62
121
  config.server = {
@@ -66,19 +125,7 @@ async function updateEntrypointConfig (logger, currentDir) {
66
125
  level: '{PLT_SERVER_LOGGER_LEVEL}'
67
126
  }
68
127
  }
69
-
70
128
  await writeFile(configPath, JSON.stringify(config, null, 2))
71
129
  }
72
130
 
73
- async function updateEntrypointEnv (port, logger, currentDir) {
74
- const env = `\
75
- PLT_SERVER_HOSTNAME=127.0.0.1
76
- PORT=${port}
77
- PLT_SERVER_LOGGER_LEVEL=info
78
- `
79
-
80
- await appendFile(join(currentDir, '.env'), env)
81
- await writeFile(join(currentDir, '.env.sample'), env)
82
- }
83
-
84
131
  export default createRuntime
@@ -1,39 +1,19 @@
1
- import { getVersion, getDependencyVersion, isFileAccessible } from '../utils.mjs'
1
+ import { getVersion, getDependencyVersion } from '../utils.mjs'
2
2
  import { createPackageJson } from '../create-package-json.mjs'
3
3
  import { createGitignore } from '../create-gitignore.mjs'
4
4
  import { getPkgManager } from '../get-pkg-manager.mjs'
5
5
  import parseArgs from 'minimist'
6
6
  import { join } from 'path'
7
7
  import inquirer from 'inquirer'
8
- import { readFile, writeFile, mkdir } from 'fs/promises'
8
+ import { mkdir, stat } from 'fs/promises'
9
9
  import pino from 'pino'
10
10
  import pretty from 'pino-pretty'
11
11
  import { execa } from 'execa'
12
12
  import ora from 'ora'
13
13
  import createService from './create-service.mjs'
14
14
  import askDir from '../ask-dir.mjs'
15
- import { askDynamicWorkspaceCreateGHAction, askStaticWorkspaceGHAction } from '../ghaction.mjs'
16
- import { getRunPackageManagerInstall, getUseTypescript, getPort, getOverwriteReadme } from '../cli-options.mjs'
17
-
18
- export const createReadme = async (logger, dir = '.') => {
19
- const readmeFileName = join(dir, 'README.md')
20
- let isReadmeExists = await isFileAccessible(readmeFileName)
21
- if (isReadmeExists) {
22
- logger.debug(`${readmeFileName} found, asking to overwrite it.`)
23
- const { shouldReplace } = await inquirer.prompt([getOverwriteReadme()])
24
- isReadmeExists = !shouldReplace
25
- }
26
-
27
- if (isReadmeExists) {
28
- logger.debug(`${readmeFileName} found, skipping creation of README.md file.`)
29
- return
30
- }
31
-
32
- const readmeFile = new URL('README.md', import.meta.url)
33
- const readme = await readFile(readmeFile, 'utf-8')
34
- await writeFile(readmeFileName, readme)
35
- logger.debug(`${readmeFileName} successfully created.`)
36
- }
15
+ import { getRunPackageManagerInstall, getUseTypescript, getPort } from '../cli-options.mjs'
16
+ import { createReadme } from '../create-readme.mjs'
37
17
 
38
18
  const createPlatformaticService = async (_args, opts = {}) => {
39
19
  const logger = opts.logger || pino(pretty({
@@ -54,10 +34,18 @@ const createPlatformaticService = async (_args, opts = {}) => {
54
34
  const version = await getVersion()
55
35
  const pkgManager = getPkgManager()
56
36
 
57
- const projectDir = opts.dir || await askDir(logger, '.')
37
+ const projectDir = opts.dir || await askDir(logger, join('.', 'platformatic-service'))
58
38
  const isRuntimeContext = opts.isRuntimeContext || false
59
39
 
60
- const toAsk = [getUseTypescript(args.typescript)]
40
+ // checks directory
41
+ try {
42
+ await stat(projectDir)
43
+ logger.error(`Directory ${projectDir} already exists. Please choose another path.`)
44
+ process.exit(1)
45
+ } catch (err) {}
46
+
47
+ const toAsk = []
48
+ toAsk.push(getUseTypescript(args.typescript))
61
49
 
62
50
  if (!isRuntimeContext) {
63
51
  toAsk.push(getPort(args.port))
@@ -67,7 +55,29 @@ const createPlatformaticService = async (_args, opts = {}) => {
67
55
  toAsk.unshift(getRunPackageManagerInstall(pkgManager))
68
56
  }
69
57
 
70
- const { runPackageManagerInstall, useTypescript, port } = await inquirer.prompt(toAsk)
58
+ if (!opts.skipGitHubActions) {
59
+ toAsk.push({
60
+ type: 'list',
61
+ name: 'staticWorkspaceGitHubAction',
62
+ message: 'Do you want to create the github action to deploy this application to Platformatic Cloud?',
63
+ default: true,
64
+ choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
65
+ },
66
+ {
67
+ type: 'list',
68
+ name: 'dynamicWorkspaceGitHubAction',
69
+ message: 'Do you want to enable PR Previews in your application?',
70
+ default: true,
71
+ choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
72
+ })
73
+ }
74
+ const {
75
+ runPackageManagerInstall,
76
+ useTypescript,
77
+ port,
78
+ staticWorkspaceGitHubAction,
79
+ dynamicWorkspaceGitHubAction
80
+ } = await inquirer.prompt(toAsk)
71
81
 
72
82
  // Create the project directory
73
83
  await mkdir(projectDir, { recursive: true })
@@ -76,10 +86,13 @@ const createPlatformaticService = async (_args, opts = {}) => {
76
86
  isRuntimeContext,
77
87
  hostname: args.hostname,
78
88
  port,
79
- typescript: useTypescript
89
+ typescript: useTypescript,
90
+ staticWorkspaceGitHubAction,
91
+ dynamicWorkspaceGitHubAction,
92
+ runtimeContext: opts.runtimeContext
80
93
  }
81
94
 
82
- const env = await createService(params, logger, projectDir, version)
95
+ await createService(params, logger, projectDir, version)
83
96
 
84
97
  const fastifyVersion = await getDependencyVersion('fastify')
85
98
 
@@ -94,27 +107,21 @@ const createPlatformaticService = async (_args, opts = {}) => {
94
107
  if (!opts.skipGitignore) {
95
108
  await createGitignore(logger, projectDir)
96
109
  }
97
- await createReadme(logger, projectDir)
110
+ await createReadme(logger, projectDir, 'service')
98
111
 
99
112
  if (runPackageManagerInstall) {
100
113
  const spinner = ora('Installing dependencies...').start()
101
114
  await execa(pkgManager, ['install'], { cwd: projectDir })
102
- spinner.succeed('...done!')
115
+ spinner.succeed()
103
116
  }
104
117
 
105
118
  const spinner = ora('Generating types...').start()
106
119
  try {
107
120
  await execa(pkgManager, ['exec', 'platformatic', 'service', 'types'], { cwd: projectDir })
108
- spinner.succeed('...done!')
121
+ spinner.succeed('Types generated!')
109
122
  } catch (err) {
110
123
  logger.trace({ err })
111
- spinner.fail('...failed! Try again by running "platformatic service types"')
112
- }
113
-
114
- if (!opts.skipGitHubActions) {
115
- await askStaticWorkspaceGHAction(logger, env, 'service', useTypescript, projectDir)
116
- await askDynamicWorkspaceCreateGHAction(logger, env, 'service', useTypescript, projectDir)
124
+ spinner.fail('Failed to generate Types. Try again by running "platformatic service types"')
117
125
  }
118
126
  }
119
-
120
127
  export default createPlatformaticService