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.
- package/create-platformatic.mjs +1 -1
- package/package.json +19 -19
- package/src/ask-dir.mjs +2 -0
- package/src/cli-options.mjs +0 -10
- package/src/composer/create-composer-cli.mjs +44 -40
- package/src/composer/create-composer.mjs +38 -16
- package/src/create-package-json.mjs +2 -1
- package/src/create-readme.mjs +12 -0
- package/src/db/create-db-cli.mjs +84 -76
- package/src/db/create-db.mjs +42 -23
- package/src/ghaction.mjs +8 -41
- package/src/runtime/create-runtime-cli.mjs +58 -42
- package/src/runtime/create-runtime.mjs +72 -25
- package/src/service/create-service-cli.mjs +46 -39
- package/src/service/create-service.mjs +40 -17
- package/src/utils.mjs +11 -0
package/src/db/create-db.mjs
CHANGED
|
@@ -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:
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
358
|
-
const
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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 (
|
|
447
|
-
|
|
465
|
+
if (isRuntimeContext) {
|
|
466
|
+
return addPrefixToEnv(isRuntimeContext)
|
|
448
467
|
}
|
|
449
|
-
return
|
|
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
|
|
10
|
-
acc += `${spaces}${key}: \${{ secrets
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
57
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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,
|
|
2
|
-
import {
|
|
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 {
|
|
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
|
-
|
|
45
|
-
await
|
|
46
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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
|
|
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 {
|
|
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 {
|
|
16
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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('
|
|
121
|
+
spinner.succeed('Types generated!')
|
|
109
122
|
} catch (err) {
|
|
110
123
|
logger.trace({ err })
|
|
111
|
-
spinner.fail('
|
|
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
|