create-platformatic 0.28.1 → 0.30.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 +3 -0
- package/package.json +6 -5
- package/src/create-gitignore.mjs +5 -1
- package/src/create-package-json.mjs +9 -0
- package/src/db/create-db-cli.mjs +73 -6
- package/src/db/create-db.mjs +37 -35
- package/src/get-tsconfig.mjs +21 -0
- package/src/ghaction.mjs +44 -11
- package/src/index.mjs +10 -1
- package/src/service/create-service.mjs +15 -25
- package/src/utils.mjs +13 -2
- package/test/db/create-db.test.mjs +2 -1
- package/test/exports.test.mjs +8 -0
- package/test/ghaction-dynamic.test.mjs +14 -10
- package/test/ghaction-static.test.mjs +14 -10
- package/test/service/create-service.test.mjs +1 -1
package/create-platformatic.mjs
CHANGED
|
@@ -36,3 +36,6 @@ export {
|
|
|
36
36
|
createStaticWorkspaceGHAction,
|
|
37
37
|
createDynamicWorkspaceGHAction
|
|
38
38
|
} from './src/ghaction.mjs'
|
|
39
|
+
|
|
40
|
+
export { createGitignore, createPackageJson, getDependencyVersion, getVersion } from './src/index.mjs'
|
|
41
|
+
export { default as createService } from './src/service/create-service.mjs'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-platformatic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Create platformatic-db interactive tool",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"boring-name-generator": "^1.0.3",
|
|
19
19
|
"chalk": "^5.2.0",
|
|
20
|
+
"columnify": "^1.6.0",
|
|
20
21
|
"commist": "^3.2.0",
|
|
21
22
|
"desm": "^1.3.0",
|
|
22
23
|
"es-main": "^1.2.0",
|
|
@@ -33,7 +34,7 @@
|
|
|
33
34
|
"semver": "^7.5.1",
|
|
34
35
|
"undici": "^5.22.1",
|
|
35
36
|
"which": "^3.0.1",
|
|
36
|
-
"@platformatic/config": "0.
|
|
37
|
+
"@platformatic/config": "0.30.0"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
40
|
"ajv": "^8.12.0",
|
|
@@ -45,11 +46,11 @@
|
|
|
45
46
|
"standard": "^17.1.0",
|
|
46
47
|
"tap": "^16.3.6",
|
|
47
48
|
"yaml": "^2.3.1",
|
|
48
|
-
"@platformatic/db": "0.
|
|
49
|
-
"@platformatic/service": "0.
|
|
49
|
+
"@platformatic/db": "0.30.0",
|
|
50
|
+
"@platformatic/service": "0.30.0"
|
|
50
51
|
},
|
|
51
52
|
"scripts": {
|
|
52
|
-
"test": "standard | snazzy && cross-env NODE_OPTIONS=\"--loader=esmock --no-warnings\" c8
|
|
53
|
+
"test": "standard | snazzy && cross-env NODE_OPTIONS=\"--loader=esmock --no-warnings\" c8 tap --no-coverage test/*test.mjs test/*/*test.mjs",
|
|
53
54
|
"lint": "standard | snazzy"
|
|
54
55
|
}
|
|
55
56
|
}
|
package/src/create-gitignore.mjs
CHANGED
|
@@ -31,7 +31,11 @@ tags
|
|
|
31
31
|
# clinicjs
|
|
32
32
|
.clinic/
|
|
33
33
|
`
|
|
34
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Creates a standard Platformatic app .gitignore file
|
|
36
|
+
* @param {import('pino').BaseLogger} logger Logger Interface
|
|
37
|
+
* @param {string} dir Target directory
|
|
38
|
+
*/
|
|
35
39
|
export const createGitignore = async (logger, dir = '.') => {
|
|
36
40
|
const gitignoreFileName = join(dir, '.gitignore')
|
|
37
41
|
const isGitignoreExists = await isFileAccessible(gitignoreFileName)
|
|
@@ -23,6 +23,15 @@ const packageJsonTemplate = (addTSBuild = false) => (`\
|
|
|
23
23
|
}
|
|
24
24
|
}`)
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Creates a Platformatic app package.json file
|
|
28
|
+
* @param {string} platVersion Platformatic Version
|
|
29
|
+
* @param {string} fastifyVersion Fastify Version
|
|
30
|
+
* @param {import('pino').BaseLogger} logger Logger Interface
|
|
31
|
+
* @param {string} dir Target directory where to create the file
|
|
32
|
+
* @param {boolean} addTSBuild Whether to add TS Build or not
|
|
33
|
+
* @param {object} scripts Package.json scripts list
|
|
34
|
+
*/
|
|
26
35
|
export const createPackageJson = async (platVersion, fastifyVersion, logger, dir, addTSBuild = false, scripts = {}) => {
|
|
27
36
|
const packageJsonFileName = join(dir, 'package.json')
|
|
28
37
|
const isPackageJsonExists = await isFileAccessible(packageJsonFileName)
|
package/src/db/create-db-cli.mjs
CHANGED
|
@@ -11,11 +11,25 @@ import pino from 'pino'
|
|
|
11
11
|
import pretty from 'pino-pretty'
|
|
12
12
|
import { execa } from 'execa'
|
|
13
13
|
import ora from 'ora'
|
|
14
|
-
import createDB from './create-db.mjs'
|
|
14
|
+
import { getConnectionString, createDB } from './create-db.mjs'
|
|
15
15
|
import askDir from '../ask-dir.mjs'
|
|
16
16
|
import { askDynamicWorkspaceCreateGHAction, askStaticWorkspaceGHAction } from '../ghaction.mjs'
|
|
17
17
|
import { getRunPackageManagerInstall, getUseTypescript, getPort, getOverwriteReadme } from '../cli-options.mjs'
|
|
18
18
|
|
|
19
|
+
const databases = [{
|
|
20
|
+
value: 'sqlite',
|
|
21
|
+
name: 'SQLite'
|
|
22
|
+
}, {
|
|
23
|
+
value: 'postgres',
|
|
24
|
+
name: 'PostgreSQL'
|
|
25
|
+
}, {
|
|
26
|
+
value: 'mysql',
|
|
27
|
+
name: 'MySQL'
|
|
28
|
+
}, {
|
|
29
|
+
value: 'mariadb',
|
|
30
|
+
name: 'MariaDB'
|
|
31
|
+
}]
|
|
32
|
+
|
|
19
33
|
export const createReadme = async (logger, dir = '.') => {
|
|
20
34
|
const readmeFileName = join(dir, 'README.md')
|
|
21
35
|
let isReadmeExists = await isFileAccessible(readmeFileName)
|
|
@@ -70,6 +84,48 @@ const createPlatformaticDB = async (_args, opts) => {
|
|
|
70
84
|
const pkgManager = getPkgManager()
|
|
71
85
|
const projectDir = opts.dir || await askDir(logger, '.')
|
|
72
86
|
|
|
87
|
+
const { database } = await inquirer.prompt({
|
|
88
|
+
type: 'list',
|
|
89
|
+
name: 'database',
|
|
90
|
+
message: 'What database do you want to use?',
|
|
91
|
+
default: args.database,
|
|
92
|
+
choices: databases
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
let connectionString = getConnectionString(database)
|
|
96
|
+
|
|
97
|
+
while (true) {
|
|
98
|
+
const pickConnectionString = await inquirer.prompt({
|
|
99
|
+
type: 'expand',
|
|
100
|
+
name: 'edit',
|
|
101
|
+
message: `Do you want to use the connection string "${connectionString}"?`,
|
|
102
|
+
choices: [
|
|
103
|
+
{
|
|
104
|
+
key: 'y',
|
|
105
|
+
name: 'Confirm',
|
|
106
|
+
value: false
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
key: 'e',
|
|
110
|
+
name: 'Edit',
|
|
111
|
+
value: true
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
if (pickConnectionString.edit) {
|
|
117
|
+
const answers = await inquirer.prompt({
|
|
118
|
+
type: 'editor',
|
|
119
|
+
name: 'connectionString',
|
|
120
|
+
message: 'Edit the connection string',
|
|
121
|
+
default: connectionString
|
|
122
|
+
})
|
|
123
|
+
connectionString = answers.connectionString.trim()
|
|
124
|
+
} else {
|
|
125
|
+
break
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
73
129
|
const wizardOptions = await inquirer.prompt([{
|
|
74
130
|
type: 'list',
|
|
75
131
|
name: 'defaultMigrations',
|
|
@@ -97,7 +153,8 @@ const createPlatformaticDB = async (_args, opts) => {
|
|
|
97
153
|
const params = {
|
|
98
154
|
hostname: args.hostname,
|
|
99
155
|
port: wizardOptions.port,
|
|
100
|
-
database
|
|
156
|
+
database,
|
|
157
|
+
connectionString,
|
|
101
158
|
migrations: wizardOptions.defaultMigrations ? args.migrations : '',
|
|
102
159
|
plugin: generatePlugin,
|
|
103
160
|
types: useTypes,
|
|
@@ -162,8 +219,13 @@ const createPlatformaticDB = async (_args, opts) => {
|
|
|
162
219
|
if (applyMigrations) {
|
|
163
220
|
const spinner = ora('Applying migrations...').start()
|
|
164
221
|
// We need to apply migrations using the platformatic installed in the project
|
|
165
|
-
|
|
166
|
-
|
|
222
|
+
try {
|
|
223
|
+
await execa(pkgManager, ['exec', 'platformatic', 'db', 'migrations', 'apply'], { cwd: projectDir })
|
|
224
|
+
spinner.succeed('...done!')
|
|
225
|
+
} catch (err) {
|
|
226
|
+
logger.trace({ err })
|
|
227
|
+
spinner.fail('...failed! Try again by running "platformatic db migrations apply"')
|
|
228
|
+
}
|
|
167
229
|
}
|
|
168
230
|
}
|
|
169
231
|
if (generatePlugin) {
|
|
@@ -177,8 +239,13 @@ const createPlatformaticDB = async (_args, opts) => {
|
|
|
177
239
|
|
|
178
240
|
if (generateTypes) {
|
|
179
241
|
const spinner = ora('Generating types...').start()
|
|
180
|
-
|
|
181
|
-
|
|
242
|
+
try {
|
|
243
|
+
await execa(pkgManager, ['exec', 'platformatic', 'db', 'types'], { cwd: projectDir })
|
|
244
|
+
spinner.succeed('...done!')
|
|
245
|
+
} catch (err) {
|
|
246
|
+
logger.trace({ err })
|
|
247
|
+
spinner.fail('...failed! Try again by running "platformatic db types"')
|
|
248
|
+
}
|
|
182
249
|
}
|
|
183
250
|
}
|
|
184
251
|
}
|
package/src/db/create-db.mjs
CHANGED
|
@@ -1,49 +1,37 @@
|
|
|
1
1
|
import { writeFile, mkdir, appendFile } from 'fs/promises'
|
|
2
2
|
import { join, relative, resolve } from 'path'
|
|
3
3
|
import { findDBConfigFile, isFileAccessible } from '../utils.mjs'
|
|
4
|
+
import { getTsConfig } from '../get-tsconfig.mjs'
|
|
4
5
|
|
|
5
6
|
const connectionStrings = {
|
|
6
|
-
postgres: 'postgres://postgres:postgres@
|
|
7
|
+
postgres: 'postgres://postgres:postgres@127.0.0.1:5432/postgres',
|
|
7
8
|
sqlite: 'sqlite://./db.sqlite',
|
|
8
|
-
mysql: 'mysql://root@
|
|
9
|
-
|
|
10
|
-
mariadb: 'mysql://root@localhost:3307/graph'
|
|
9
|
+
mysql: 'mysql://root@127.0.0.1:3306/platformatic',
|
|
10
|
+
mariadb: 'mysql://root@127.0.0.1:3306/platformatic'
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const moviesMigrationDo =
|
|
13
|
+
const moviesMigrationDo = (database) => {
|
|
14
|
+
const key = {
|
|
15
|
+
postgres: 'SERIAL',
|
|
16
|
+
sqlite: 'INTEGER',
|
|
17
|
+
mysql: 'INTEGER UNSIGNED AUTO_INCREMENT',
|
|
18
|
+
mariadb: 'INTEGER UNSIGNED AUTO_INCREMENT'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return `
|
|
14
22
|
-- Add SQL in this file to create the database tables for your API
|
|
15
23
|
CREATE TABLE IF NOT EXISTS movies (
|
|
16
|
-
id
|
|
24
|
+
id ${key[database]} PRIMARY KEY,
|
|
17
25
|
title TEXT NOT NULL
|
|
18
26
|
);
|
|
19
27
|
`
|
|
28
|
+
}
|
|
20
29
|
|
|
21
30
|
const moviesMigrationUndo = `
|
|
22
31
|
-- Add SQL in this file to drop the database tables
|
|
23
32
|
DROP TABLE movies;
|
|
24
33
|
`
|
|
25
34
|
|
|
26
|
-
function getTsConfig (outDir) {
|
|
27
|
-
return {
|
|
28
|
-
compilerOptions: {
|
|
29
|
-
module: 'commonjs',
|
|
30
|
-
esModuleInterop: true,
|
|
31
|
-
target: 'es6',
|
|
32
|
-
sourceMap: true,
|
|
33
|
-
pretty: true,
|
|
34
|
-
noEmitOnError: true,
|
|
35
|
-
outDir
|
|
36
|
-
},
|
|
37
|
-
watchOptions: {
|
|
38
|
-
watchFile: 'fixedPollingInterval',
|
|
39
|
-
watchDirectory: 'fixedPollingInterval',
|
|
40
|
-
fallbackPolling: 'dynamicPriority',
|
|
41
|
-
synchronousWatchDirectory: true,
|
|
42
|
-
excludeDirectories: ['**/node_modules', outDir]
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
35
|
const getPluginName = (isTypescript) => isTypescript === true ? 'plugin.ts' : 'plugin.js'
|
|
48
36
|
const TS_OUT_DIR = 'dist'
|
|
49
37
|
|
|
@@ -86,20 +74,29 @@ function generateConfig (migrations, plugin, types, typescript, version) {
|
|
|
86
74
|
}
|
|
87
75
|
|
|
88
76
|
if (typescript === true) {
|
|
89
|
-
config.plugins.typescript =
|
|
77
|
+
config.plugins.typescript = '{PLT_TYPESCRIPT}'
|
|
90
78
|
}
|
|
91
79
|
|
|
92
80
|
return config
|
|
93
81
|
}
|
|
94
82
|
|
|
95
|
-
function generateEnv (hostname, port,
|
|
96
|
-
|
|
97
|
-
const env = `\
|
|
83
|
+
function generateEnv (hostname, port, connectionString, typescript) {
|
|
84
|
+
let env = `\
|
|
98
85
|
PLT_SERVER_HOSTNAME=${hostname}
|
|
99
86
|
PORT=${port}
|
|
100
87
|
PLT_SERVER_LOGGER_LEVEL=info
|
|
101
88
|
DATABASE_URL=${connectionString}
|
|
102
89
|
`
|
|
90
|
+
|
|
91
|
+
if (typescript === true) {
|
|
92
|
+
env += `\
|
|
93
|
+
|
|
94
|
+
# Set to false to disable automatic typescript compilation.
|
|
95
|
+
# Changing this setting is needed for production
|
|
96
|
+
PLT_TYPESCRIPT=true
|
|
97
|
+
`
|
|
98
|
+
}
|
|
99
|
+
|
|
103
100
|
return env
|
|
104
101
|
}
|
|
105
102
|
|
|
@@ -135,7 +132,12 @@ async function generatePluginWithTypesSupport (logger, currentDir, isTypescript)
|
|
|
135
132
|
logger.info(`Plugin file created at ${relative(currentDir, pluginPath)}`)
|
|
136
133
|
}
|
|
137
134
|
|
|
138
|
-
|
|
135
|
+
export function getConnectionString (database) {
|
|
136
|
+
return connectionStrings[database]
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export async function createDB ({ hostname, database = 'sqlite', port, migrations = 'migrations', plugin = true, types = true, typescript = false, connectionString }, logger, currentDir, version) {
|
|
140
|
+
connectionString = connectionString || getConnectionString(database)
|
|
139
141
|
const createMigrations = !!migrations // If we don't define a migrations folder, we don't create it
|
|
140
142
|
const accessibleConfigFilename = await findDBConfigFile(currentDir)
|
|
141
143
|
if (accessibleConfigFilename === undefined) {
|
|
@@ -143,7 +145,7 @@ async function createDB ({ hostname, database = 'sqlite', port, migrations = 'mi
|
|
|
143
145
|
await writeFile(join(currentDir, 'platformatic.db.json'), JSON.stringify(config, null, 2))
|
|
144
146
|
logger.info('Configuration file platformatic.db.json successfully created.')
|
|
145
147
|
|
|
146
|
-
const env = generateEnv(hostname, port,
|
|
148
|
+
const env = generateEnv(hostname, port, connectionString, typescript)
|
|
147
149
|
const envFileExists = await isFileAccessible('.env', currentDir)
|
|
148
150
|
await appendFile(join(currentDir, '.env'), env)
|
|
149
151
|
await writeFile(join(currentDir, '.env.sample'), env)
|
|
@@ -175,7 +177,7 @@ async function createDB ({ hostname, database = 'sqlite', port, migrations = 'mi
|
|
|
175
177
|
const isMigrationFileDoExists = await isFileAccessible(migrationFilePathDo)
|
|
176
178
|
const isMigrationFileUndoExists = await isFileAccessible(migrationFilePathUndo)
|
|
177
179
|
if (!isMigrationFileDoExists && createMigrations) {
|
|
178
|
-
await writeFile(migrationFilePathDo, moviesMigrationDo)
|
|
180
|
+
await writeFile(migrationFilePathDo, moviesMigrationDo(database))
|
|
179
181
|
logger.info(`Migration file ${migrationFileNameDo} successfully created.`)
|
|
180
182
|
if (!isMigrationFileUndoExists) {
|
|
181
183
|
await writeFile(migrationFilePathUndo, moviesMigrationUndo)
|
|
@@ -202,7 +204,7 @@ async function createDB ({ hostname, database = 'sqlite', port, migrations = 'mi
|
|
|
202
204
|
}
|
|
203
205
|
|
|
204
206
|
return {
|
|
205
|
-
DATABASE_URL:
|
|
207
|
+
DATABASE_URL: connectionString,
|
|
206
208
|
PLT_SERVER_LOGGER_LEVEL: 'info',
|
|
207
209
|
PORT: port,
|
|
208
210
|
PLT_SERVER_HOSTNAME: hostname
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function getTsConfig (outDir) {
|
|
2
|
+
return {
|
|
3
|
+
compilerOptions: {
|
|
4
|
+
module: 'commonjs',
|
|
5
|
+
esModuleInterop: true,
|
|
6
|
+
target: 'es2020',
|
|
7
|
+
sourceMap: true,
|
|
8
|
+
pretty: true,
|
|
9
|
+
noEmitOnError: true,
|
|
10
|
+
incremental: true,
|
|
11
|
+
outDir
|
|
12
|
+
},
|
|
13
|
+
watchOptions: {
|
|
14
|
+
watchFile: 'fixedPollingInterval',
|
|
15
|
+
watchDirectory: 'fixedPollingInterval',
|
|
16
|
+
fallbackPolling: 'dynamicPriority',
|
|
17
|
+
synchronousWatchDirectory: true,
|
|
18
|
+
excludeDirectories: ['**/node_modules', outDir]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/ghaction.mjs
CHANGED
|
@@ -2,12 +2,36 @@ import { join } from 'path'
|
|
|
2
2
|
import inquirer from 'inquirer'
|
|
3
3
|
import { isFileAccessible } from './utils.mjs'
|
|
4
4
|
import { writeFile, mkdir } from 'fs/promises'
|
|
5
|
+
import columnify from 'columnify'
|
|
6
|
+
function envAsString (env) {
|
|
7
|
+
return Object.keys(env).reduce((acc, key) => {
|
|
8
|
+
if (key === 'DATABASE_URL') {
|
|
9
|
+
acc += ` ${key}: \${{ secrets.DATABASE_URL }}\n`
|
|
10
|
+
} else {
|
|
11
|
+
acc += ` ${key}: ${env[key]} \n`
|
|
12
|
+
}
|
|
5
13
|
|
|
6
|
-
export const dynamicWorkspaceGHTemplate = (env, config, buildTS = false) => {
|
|
7
|
-
const envAsStr = Object.keys(env).reduce((acc, key) => {
|
|
8
|
-
acc += ` ${key}: ${env[key]} \n`
|
|
9
14
|
return acc
|
|
10
15
|
}, '')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function formatSecretsToAdd (secrets) {
|
|
19
|
+
const output = columnify(secrets, {
|
|
20
|
+
showHeaders: false,
|
|
21
|
+
columnSplitter: ': ',
|
|
22
|
+
config: {
|
|
23
|
+
key: {
|
|
24
|
+
align: 'right'
|
|
25
|
+
},
|
|
26
|
+
value: {
|
|
27
|
+
align: 'left'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
return output
|
|
32
|
+
}
|
|
33
|
+
export const dynamicWorkspaceGHTemplate = (env, config, buildTS = false) => {
|
|
34
|
+
const envString = envAsString(env)
|
|
11
35
|
|
|
12
36
|
return `name: Deploy Platformatic application to the cloud
|
|
13
37
|
on:
|
|
@@ -39,15 +63,12 @@ jobs:
|
|
|
39
63
|
platformatic_workspace_key: \${{ secrets.PLATFORMATIC_DYNAMIC_WORKSPACE_API_KEY }}
|
|
40
64
|
platformatic_config_path: ${config}
|
|
41
65
|
env:
|
|
42
|
-
${
|
|
66
|
+
${envString}
|
|
43
67
|
`
|
|
44
68
|
}
|
|
45
69
|
|
|
46
70
|
export const staticWorkspaceGHTemplate = (env, config, buildTS = false) => {
|
|
47
|
-
const
|
|
48
|
-
acc += ` ${key}: ${env[key]} \n`
|
|
49
|
-
return acc
|
|
50
|
-
}, '')
|
|
71
|
+
const envString = envAsString(env)
|
|
51
72
|
|
|
52
73
|
return `name: Deploy Platformatic application to the cloud
|
|
53
74
|
on:
|
|
@@ -80,7 +101,7 @@ jobs:
|
|
|
80
101
|
platformatic_workspace_key: \${{ secrets.PLATFORMATIC_STATIC_WORKSPACE_API_KEY }}
|
|
81
102
|
platformatic_config_path: ${config}
|
|
82
103
|
env:
|
|
83
|
-
${
|
|
104
|
+
${envString}
|
|
84
105
|
`
|
|
85
106
|
}
|
|
86
107
|
|
|
@@ -91,7 +112,13 @@ export const createDynamicWorkspaceGHAction = async (logger, env, config, projec
|
|
|
91
112
|
if (!isGithubActionExists) {
|
|
92
113
|
await mkdir(join(projectDir, '.github', 'workflows'), { recursive: true })
|
|
93
114
|
await writeFile(ghActionFilePath, dynamicWorkspaceGHTemplate(env, config, buildTS))
|
|
94
|
-
logger.info('Github action successfully created, please add
|
|
115
|
+
logger.info('Github action successfully created, please add the following secrets as repository secrets: ')
|
|
116
|
+
const secretsString = formatSecretsToAdd({
|
|
117
|
+
PLATFORMATIC_DYNAMIC_WORKSPACE_ID: 'your workspace id',
|
|
118
|
+
PLATFORMATIC_DYNAMIC_WORKSPACE_API_KEY: 'your workspace API key',
|
|
119
|
+
DATABASE_URL: env.DATABASE_URL
|
|
120
|
+
})
|
|
121
|
+
logger.info(`\n ${secretsString}`)
|
|
95
122
|
const isGitDir = await isFileAccessible('.git', projectDir)
|
|
96
123
|
if (!isGitDir) {
|
|
97
124
|
logger.warn('No git repository found. The Github action won\'t be triggered.')
|
|
@@ -126,7 +153,13 @@ export const createStaticWorkspaceGHAction = async (logger, env, config, project
|
|
|
126
153
|
if (!isGithubActionExists) {
|
|
127
154
|
await mkdir(join(projectDir, '.github', 'workflows'), { recursive: true })
|
|
128
155
|
await writeFile(ghActionFilePath, staticWorkspaceGHTemplate(env, config, buildTS))
|
|
129
|
-
logger.info('Github action successfully created, please add
|
|
156
|
+
logger.info('Github action successfully created, please add the following secrets as repository secrets: ')
|
|
157
|
+
const secretsString = formatSecretsToAdd({
|
|
158
|
+
PLATFORMATIC_STATIC_WORKSPACE_ID: 'your workspace id',
|
|
159
|
+
PLATFORMATIC_STATIC_WORKSPACE_API_KEY: 'your workspace API key',
|
|
160
|
+
DATABASE_URL: env.DATABASE_URL
|
|
161
|
+
})
|
|
162
|
+
logger.info(`\n ${secretsString}`)
|
|
130
163
|
const isGitDir = await isFileAccessible('.git', projectDir)
|
|
131
164
|
if (!isGitDir) {
|
|
132
165
|
logger.warn('No git repository found. The Github action won\'t be triggered.')
|
package/src/index.mjs
CHANGED
|
@@ -8,7 +8,9 @@ import createPlatformaticService from './service/create-service-cli.mjs'
|
|
|
8
8
|
import createPlatformaticComposer from './composer/create-composer-cli.mjs'
|
|
9
9
|
import { createPlatformaticRuntime, createRuntimeService } from './runtime/create-runtime-cli.mjs'
|
|
10
10
|
import commist from 'commist'
|
|
11
|
-
import { getUsername, getVersion, minimumSupportedNodeVersions, isCurrentVersionSupported, findRuntimeConfigFile } from './utils.mjs'
|
|
11
|
+
import { getUsername, getVersion, minimumSupportedNodeVersions, isCurrentVersionSupported, findRuntimeConfigFile, getDependencyVersion } from './utils.mjs'
|
|
12
|
+
import { createPackageJson } from './create-package-json.mjs'
|
|
13
|
+
import { createGitignore } from './create-gitignore.mjs'
|
|
12
14
|
|
|
13
15
|
export async function chooseKind (argv, opts = {}) {
|
|
14
16
|
const skip = opts.skip
|
|
@@ -101,3 +103,10 @@ const createPlatformatic = async (argv) => {
|
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
export default createPlatformatic
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
createPackageJson,
|
|
109
|
+
createGitignore,
|
|
110
|
+
getVersion,
|
|
111
|
+
getDependencyVersion
|
|
112
|
+
}
|
|
@@ -2,6 +2,7 @@ import { writeFile, mkdir, readFile, appendFile } from 'fs/promises'
|
|
|
2
2
|
import { join } from 'path'
|
|
3
3
|
import * as desm from 'desm'
|
|
4
4
|
import { findServiceConfigFile, isFileAccessible } from '../utils.mjs'
|
|
5
|
+
import { getTsConfig } from '../get-tsconfig.mjs'
|
|
5
6
|
|
|
6
7
|
const TS_OUT_DIR = 'dist'
|
|
7
8
|
|
|
@@ -29,18 +30,28 @@ function generateConfig (version, typescript) {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
if (typescript === true) {
|
|
32
|
-
config.plugins.typescript =
|
|
33
|
+
config.plugins.typescript = '{PLT_TYPESCRIPT}'
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
return config
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
function generateEnv (hostname, port) {
|
|
39
|
-
|
|
39
|
+
function generateEnv (hostname, port, typescript) {
|
|
40
|
+
let env = `\
|
|
40
41
|
PLT_SERVER_HOSTNAME=${hostname}
|
|
41
42
|
PORT=${port}
|
|
42
43
|
PLT_SERVER_LOGGER_LEVEL=info
|
|
43
44
|
`
|
|
45
|
+
|
|
46
|
+
if (typescript === true) {
|
|
47
|
+
env += `\
|
|
48
|
+
|
|
49
|
+
# Set to false to disable automatic typescript compilation.
|
|
50
|
+
# Changing this setting is needed for production
|
|
51
|
+
PLT_TYPESCRIPT=true
|
|
52
|
+
`
|
|
53
|
+
}
|
|
54
|
+
|
|
44
55
|
return env
|
|
45
56
|
}
|
|
46
57
|
|
|
@@ -90,27 +101,6 @@ export default async function (fastify: FastifyInstance, opts: FastifyPluginOpti
|
|
|
90
101
|
}
|
|
91
102
|
`
|
|
92
103
|
|
|
93
|
-
function getTsConfig (outDir) {
|
|
94
|
-
return {
|
|
95
|
-
compilerOptions: {
|
|
96
|
-
module: 'commonjs',
|
|
97
|
-
esModuleInterop: true,
|
|
98
|
-
target: 'es6',
|
|
99
|
-
sourceMap: true,
|
|
100
|
-
pretty: true,
|
|
101
|
-
noEmitOnError: true,
|
|
102
|
-
outDir
|
|
103
|
-
},
|
|
104
|
-
watchOptions: {
|
|
105
|
-
watchFile: 'fixedPollingInterval',
|
|
106
|
-
watchDirectory: 'fixedPollingInterval',
|
|
107
|
-
fallbackPolling: 'dynamicPriority',
|
|
108
|
-
synchronousWatchDirectory: true,
|
|
109
|
-
excludeDirectories: ['**/node_modules', outDir]
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
104
|
async function generatePluginWithTypesSupport (logger, currentDir, isTypescript) {
|
|
115
105
|
await mkdir(join(currentDir, 'plugins'))
|
|
116
106
|
const pluginTemplate = isTypescript
|
|
@@ -147,7 +137,7 @@ async function createService ({ hostname, port, typescript = false }, logger, cu
|
|
|
147
137
|
await writeFile(join(currentDir, 'platformatic.service.json'), JSON.stringify(config, null, 2))
|
|
148
138
|
logger.info('Configuration file platformatic.service.json successfully created.')
|
|
149
139
|
|
|
150
|
-
const env = generateEnv(hostname, port)
|
|
140
|
+
const env = generateEnv(hostname, port, typescript)
|
|
151
141
|
const envFileExists = await isFileAccessible('.env', currentDir)
|
|
152
142
|
await appendFile(join(currentDir, '.env'), env)
|
|
153
143
|
await writeFile(join(currentDir, '.env.sample'), env)
|
package/src/utils.mjs
CHANGED
|
@@ -18,7 +18,10 @@ export async function isFileAccessible (filename, directory) {
|
|
|
18
18
|
return false
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Gets the username from git config or `whoami` command
|
|
23
|
+
* @returns string | null
|
|
24
|
+
*/
|
|
22
25
|
export const getUsername = async () => {
|
|
23
26
|
try {
|
|
24
27
|
const { stdout } = await execa('git', ['config', 'user.name'])
|
|
@@ -39,7 +42,10 @@ export const getUsername = async () => {
|
|
|
39
42
|
|
|
40
43
|
return null
|
|
41
44
|
}
|
|
42
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Get the platformatic package version from package.json
|
|
47
|
+
* @returns string
|
|
48
|
+
*/
|
|
43
49
|
/* c8 ignore next 4 */
|
|
44
50
|
export const getVersion = async () => {
|
|
45
51
|
const data = await readFile(desm.join(import.meta.url, '..', 'package.json'), 'utf8')
|
|
@@ -73,6 +79,11 @@ export const findServiceConfigFile = async (directory) => (ConfigManager.findCon
|
|
|
73
79
|
export const findComposerConfigFile = async (directory) => (ConfigManager.findConfigFile(directory, 'composer'))
|
|
74
80
|
export const findRuntimeConfigFile = async (directory) => (ConfigManager.findConfigFile(directory, 'runtime'))
|
|
75
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Gets the version of the specified dependency package from package.json
|
|
84
|
+
* @param {string} dependencyName
|
|
85
|
+
* @returns string
|
|
86
|
+
*/
|
|
76
87
|
export const getDependencyVersion = async (dependencyName) => {
|
|
77
88
|
const require = createRequire(import.meta.url)
|
|
78
89
|
const pathToPackageJson = join(dirname(require.resolve(dependencyName)), 'package.json')
|
|
@@ -188,6 +188,7 @@ test('creates project with typescript', async ({ equal, same }) => {
|
|
|
188
188
|
equal(process.env.PLT_SERVER_HOSTNAME, 'myhost')
|
|
189
189
|
equal(process.env.PORT, '6666')
|
|
190
190
|
equal(process.env.DATABASE_URL, 'sqlite://./db.sqlite')
|
|
191
|
+
equal(process.env.PLT_TYPESCRIPT, 'true')
|
|
191
192
|
|
|
192
193
|
equal(db.graphql, true)
|
|
193
194
|
equal(db.openapi, true)
|
|
@@ -199,7 +200,7 @@ test('creates project with typescript', async ({ equal, same }) => {
|
|
|
199
200
|
equal(migrationFileUndo, moviesMigrationUndo)
|
|
200
201
|
|
|
201
202
|
same(plugins.paths, ['plugin.ts'])
|
|
202
|
-
equal(plugins.typescript,
|
|
203
|
+
equal(plugins.typescript, '{PLT_TYPESCRIPT}')
|
|
203
204
|
equal(await isFileAccessible(join(tmpDir, 'plugin.ts')), true)
|
|
204
205
|
equal(await isFileAccessible(join(tmpDir, 'tsconfig.json')), true)
|
|
205
206
|
})
|
|
@@ -29,9 +29,9 @@ const env = {
|
|
|
29
29
|
PLT_SERVER_LOGGER_LEVEL: 'info'
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
test('creates gh action', async ({
|
|
32
|
+
test('creates gh action', async ({ equal, match }) => {
|
|
33
33
|
await createDynamicWorkspaceGHAction(fakeLogger, env, 'db', tmpDir, false)
|
|
34
|
-
equal(log[0], 'Github action successfully created, please add
|
|
34
|
+
equal(log[0], 'Github action successfully created, please add the following secrets as repository secrets: ')
|
|
35
35
|
const accessible = await isFileAccessible(join(tmpDir, '.github/workflows/platformatic-dynamic-workspace-deploy.yml'))
|
|
36
36
|
equal(accessible, true)
|
|
37
37
|
const ghFile = await readFile(join(tmpDir, '.github/workflows/platformatic-dynamic-workspace-deploy.yml'), 'utf8')
|
|
@@ -41,16 +41,16 @@ test('creates gh action', async ({ end, equal }) => {
|
|
|
41
41
|
equal(steps[0].name, 'Checkout application project repository')
|
|
42
42
|
equal(steps[1].name, 'npm install --omit=dev')
|
|
43
43
|
equal(steps[2].name, 'Deploy project')
|
|
44
|
-
|
|
44
|
+
match(steps[2].env.DATABASE_URL, /\$\{\{ secrets.DATABASE_URL \}\}/)
|
|
45
45
|
equal(steps[2].env.PLT_SERVER_LOGGER_LEVEL, 'info')
|
|
46
46
|
|
|
47
47
|
equal(permissions.contents, 'read')
|
|
48
48
|
equal(permissions['pull-requests'], 'write')
|
|
49
49
|
})
|
|
50
50
|
|
|
51
|
-
test('creates gh action with TS build step', async ({
|
|
51
|
+
test('creates gh action with TS build step', async ({ equal, match }) => {
|
|
52
52
|
await createDynamicWorkspaceGHAction(fakeLogger, env, 'db', tmpDir, true)
|
|
53
|
-
equal(log[0], 'Github action successfully created, please add
|
|
53
|
+
equal(log[0], 'Github action successfully created, please add the following secrets as repository secrets: ')
|
|
54
54
|
const accessible = await isFileAccessible(join(tmpDir, '.github/workflows/platformatic-dynamic-workspace-deploy.yml'))
|
|
55
55
|
equal(accessible, true)
|
|
56
56
|
const ghFile = await readFile(join(tmpDir, '.github/workflows/platformatic-dynamic-workspace-deploy.yml'), 'utf8')
|
|
@@ -61,7 +61,7 @@ test('creates gh action with TS build step', async ({ end, equal }) => {
|
|
|
61
61
|
equal(steps[1].name, 'npm install --omit=dev')
|
|
62
62
|
equal(steps[2].name, 'Build project')
|
|
63
63
|
equal(steps[3].name, 'Deploy project')
|
|
64
|
-
|
|
64
|
+
match(steps[3].env.DATABASE_URL, /\$\{\{ secrets.DATABASE_URL \}\}/)
|
|
65
65
|
equal(steps[3].env.PLT_SERVER_LOGGER_LEVEL, 'info')
|
|
66
66
|
|
|
67
67
|
equal(permissions.contents, 'read')
|
|
@@ -78,17 +78,21 @@ test('do not create gitignore file because already present', async ({ end, equal
|
|
|
78
78
|
|
|
79
79
|
test('creates gh action with a warn if a .git folder is not present', async ({ end, equal }) => {
|
|
80
80
|
await createDynamicWorkspaceGHAction(fakeLogger, env, 'db', tmpDir)
|
|
81
|
-
equal(log[0], 'Github action successfully created, please add
|
|
81
|
+
equal(log[0], 'Github action successfully created, please add the following secrets as repository secrets: ')
|
|
82
82
|
const accessible = await isFileAccessible(join(tmpDir, '.github/workflows/platformatic-dynamic-workspace-deploy.yml'))
|
|
83
83
|
equal(accessible, true)
|
|
84
|
-
|
|
84
|
+
const secretsLogLine = log[1].split('\n')
|
|
85
|
+
equal(secretsLogLine[1].trim(), 'PLATFORMATIC_DYNAMIC_WORKSPACE_ID: your workspace id')
|
|
86
|
+
equal(secretsLogLine[2].trim(), 'PLATFORMATIC_DYNAMIC_WORKSPACE_API_KEY: your workspace API key')
|
|
87
|
+
equal(secretsLogLine[3].trim(), 'DATABASE_URL: mydbconnectionstring')
|
|
88
|
+
equal(log[2], 'No git repository found. The Github action won\'t be triggered.')
|
|
85
89
|
})
|
|
86
90
|
|
|
87
91
|
test('creates gh action without a warn if a .git folder is present', async ({ end, equal }) => {
|
|
88
92
|
await mkdir(join(tmpDir, '.git'), { recursive: true })
|
|
89
93
|
await createDynamicWorkspaceGHAction(fakeLogger, env, 'db', tmpDir)
|
|
90
|
-
equal(log[0], 'Github action successfully created, please add
|
|
94
|
+
equal(log[0], 'Github action successfully created, please add the following secrets as repository secrets: ')
|
|
91
95
|
const accessible = await isFileAccessible(join(tmpDir, '.github/workflows/platformatic-dynamic-workspace-deploy.yml'))
|
|
92
96
|
equal(accessible, true)
|
|
93
|
-
equal(log.length,
|
|
97
|
+
equal(log.length, 2)
|
|
94
98
|
})
|
|
@@ -29,9 +29,9 @@ const env = {
|
|
|
29
29
|
PLT_SERVER_LOGGER_LEVEL: 'info'
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
test('creates gh action', async ({
|
|
32
|
+
test('creates gh action', async ({ equal, match }) => {
|
|
33
33
|
await createStaticWorkspaceGHAction(fakeLogger, env, 'db', tmpDir, false)
|
|
34
|
-
equal(log[0], 'Github action successfully created, please add
|
|
34
|
+
equal(log[0], 'Github action successfully created, please add the following secrets as repository secrets: ')
|
|
35
35
|
const accessible = await isFileAccessible(join(tmpDir, '.github/workflows/platformatic-static-workspace-deploy.yml'))
|
|
36
36
|
equal(accessible, true)
|
|
37
37
|
const ghFile = await readFile(join(tmpDir, '.github/workflows/platformatic-static-workspace-deploy.yml'), 'utf8')
|
|
@@ -41,15 +41,15 @@ test('creates gh action', async ({ end, equal }) => {
|
|
|
41
41
|
equal(steps[0].name, 'Checkout application project repository')
|
|
42
42
|
equal(steps[1].name, 'npm install --omit=dev')
|
|
43
43
|
equal(steps[2].name, 'Deploy project')
|
|
44
|
-
|
|
44
|
+
match(steps[2].env.DATABASE_URL, /\$\{\{ secrets.DATABASE_URL \}\}/)
|
|
45
45
|
equal(steps[2].env.PLT_SERVER_LOGGER_LEVEL, 'info')
|
|
46
46
|
|
|
47
47
|
equal(permissions.contents, 'read')
|
|
48
48
|
})
|
|
49
49
|
|
|
50
|
-
test('creates gh action with TS build step', async ({
|
|
50
|
+
test('creates gh action with TS build step', async ({ equal, match }) => {
|
|
51
51
|
await createStaticWorkspaceGHAction(fakeLogger, env, 'db', tmpDir, true)
|
|
52
|
-
equal(log[0], 'Github action successfully created, please add
|
|
52
|
+
equal(log[0], 'Github action successfully created, please add the following secrets as repository secrets: ')
|
|
53
53
|
const accessible = await isFileAccessible(join(tmpDir, '.github/workflows/platformatic-static-workspace-deploy.yml'))
|
|
54
54
|
equal(accessible, true)
|
|
55
55
|
const ghFile = await readFile(join(tmpDir, '.github/workflows/platformatic-static-workspace-deploy.yml'), 'utf8')
|
|
@@ -60,7 +60,7 @@ test('creates gh action with TS build step', async ({ end, equal }) => {
|
|
|
60
60
|
equal(steps[1].name, 'npm install --omit=dev')
|
|
61
61
|
equal(steps[2].name, 'Build project')
|
|
62
62
|
equal(steps[3].name, 'Deploy project')
|
|
63
|
-
|
|
63
|
+
match(steps[3].env.DATABASE_URL, /\$\{\{ secrets.DATABASE_URL \}\}/)
|
|
64
64
|
equal(steps[3].env.PLT_SERVER_LOGGER_LEVEL, 'info')
|
|
65
65
|
|
|
66
66
|
equal(permissions.contents, 'read')
|
|
@@ -76,17 +76,21 @@ test('do not create gitignore file because already present', async ({ end, equal
|
|
|
76
76
|
|
|
77
77
|
test('creates gh action with a warn if a .git folder is not present', async ({ end, equal }) => {
|
|
78
78
|
await createStaticWorkspaceGHAction(fakeLogger, env, 'db', tmpDir)
|
|
79
|
-
equal(log[0], 'Github action successfully created, please add
|
|
79
|
+
equal(log[0], 'Github action successfully created, please add the following secrets as repository secrets: ')
|
|
80
80
|
const accessible = await isFileAccessible(join(tmpDir, '.github/workflows/platformatic-static-workspace-deploy.yml'))
|
|
81
81
|
equal(accessible, true)
|
|
82
|
-
|
|
82
|
+
const secretsLogLine = log[1].split('\n')
|
|
83
|
+
equal(secretsLogLine[1].trim(), 'PLATFORMATIC_STATIC_WORKSPACE_ID: your workspace id')
|
|
84
|
+
equal(secretsLogLine[2].trim(), 'PLATFORMATIC_STATIC_WORKSPACE_API_KEY: your workspace API key')
|
|
85
|
+
equal(secretsLogLine[3].trim(), 'DATABASE_URL: mydbconnectionstring')
|
|
86
|
+
equal(log[2], 'No git repository found. The Github action won\'t be triggered.')
|
|
83
87
|
})
|
|
84
88
|
|
|
85
89
|
test('creates gh action without a warn if a .git folder is present', async ({ end, equal }) => {
|
|
86
90
|
await mkdir(join(tmpDir, '.git'), { recursive: true })
|
|
87
91
|
await createStaticWorkspaceGHAction(fakeLogger, env, 'db', tmpDir)
|
|
88
|
-
equal(log[0], 'Github action successfully created, please add
|
|
92
|
+
equal(log[0], 'Github action successfully created, please add the following secrets as repository secrets: ')
|
|
89
93
|
const accessible = await isFileAccessible(join(tmpDir, '.github/workflows/platformatic-static-workspace-deploy.yml'))
|
|
90
94
|
equal(accessible, true)
|
|
91
|
-
equal(log.length,
|
|
95
|
+
equal(log.length, 2)
|
|
92
96
|
})
|
|
@@ -60,7 +60,7 @@ test('creates service with typescript', async ({ equal, same, ok }) => {
|
|
|
60
60
|
equal(process.env.PORT, '6666')
|
|
61
61
|
|
|
62
62
|
same(plugins.paths, [{ path: './plugins', encapsulate: false }, './routes'])
|
|
63
|
-
equal(plugins.typescript,
|
|
63
|
+
equal(plugins.typescript, '{PLT_TYPESCRIPT}')
|
|
64
64
|
|
|
65
65
|
ok(await isFileAccessible(join(tmpDir, 'tsconfig.json')))
|
|
66
66
|
ok(await isFileAccessible(join(tmpDir, 'plugins', 'example.ts')))
|