platformatic 1.6.0 → 1.7.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/help/deploy.txt CHANGED
@@ -21,3 +21,44 @@ Options:
21
21
  > :information_source:
22
22
  >
23
23
  > When deploying an application to a ***dynamic workspace***, specify the deploy `--label` option. You can find it on your cloud dashboard or you can specify a new one.
24
+ >
25
+ > If you do not specify an environment file to use with the `-e` flag, **ensure that a default environment file named `.env` exists**.
26
+
27
+ Deploy a **static** Platformatic Cloud application.
28
+
29
+ ```bash
30
+ platformatic deploy \
31
+ -t static \
32
+ -c platformatic.db.json \
33
+ -e .env.prototype \
34
+ --workspace-id=00000000-0000-0000-0000-000000000000 \
35
+ --workspace-key=11111111111111111111111111111111
36
+ ```
37
+
38
+ Deploy a **static** Platformatic Cloud application with a workspace keys file. The keys file can be downloaded from the Platformatic Console when generating a new API key.
39
+
40
+ ```bash
41
+ platformatic deploy \
42
+ -t static \
43
+ -c platformatic.db.json \
44
+ -k foo.plt.txt
45
+ ```
46
+
47
+ The `foo.plt.txt` must contain two variables for the workspace id and workspace API key.
48
+
49
+ ```
50
+ # Contents of foo.plt.txt
51
+ PLATFORMATIC_STATIC_WORKSPACE_ID=00000000-0000-0000-0000-000000000000
52
+ PLATFORMATIC_STATIC_WORKSPACE_API_KEY=11111111111111111111111111111111
53
+ ```
54
+
55
+ Deploy a **dynamic** Platformatic Cloud application.
56
+
57
+ ```bash
58
+ platformatic deploy \
59
+ -t dynamic \
60
+ -c platformatic.db.json \
61
+ -l dev \
62
+ --workspace-id=00000000-0000-0000-0000-000000000000 \
63
+ --workspace-key=11111111111111111111111111111111
64
+ ```
package/help/help.txt CHANGED
@@ -9,3 +9,4 @@ Welcome to Platformatic. Available commands are:
9
9
  * `deploy` - deploy a Platformatic application to the cloud.
10
10
  * `runtime` - start Platformatic Runtime; type `platformatic runtime help` to know more.
11
11
  * `start` - start a Platformatic application.
12
+ * `login` - generate a Platformatic login api key.
package/help/login.txt ADDED
@@ -0,0 +1,10 @@
1
+ Generate a Platformatic login api key.
2
+
3
+ ``` bash
4
+ $ platformatic deploy
5
+ ```
6
+
7
+ Options:
8
+
9
+ * `-c, --config FILE` - Specify a path to a global platformatic config file. Defaults to `~/.platformatic/config.json`.
10
+ * `--browser` - Automatically open default browser. If process stdout is a TTY, the default is `true`. Otherwise, the default is `false`.
package/lib/deploy.js CHANGED
@@ -3,12 +3,14 @@
3
3
  import { isAbsolute, dirname, relative, join } from 'path'
4
4
  import { readFile } from 'fs/promises'
5
5
 
6
+ import { request } from 'undici'
6
7
  import pino from 'pino'
7
8
  import pretty from 'pino-pretty'
8
9
  import dotenv from 'dotenv'
9
10
  import inquirer from 'inquirer'
10
11
  import parseArgs from 'minimist'
11
12
  import deployClient from '@platformatic/deploy-client'
13
+ import { getUserApiKey } from '@platformatic/authenticate'
12
14
  import errors from './errors.js'
13
15
 
14
16
  export const DEPLOY_SERVICE_HOST = 'https://deploy.platformatic.cloud'
@@ -21,8 +23,12 @@ const logger = pino(pretty({
21
23
  ignore: 'hostname,pid'
22
24
  }))
23
25
 
24
- async function askWorkspaceDetails (args) {
25
- let workspaceType = args.type
26
+ async function askMissingWorkspaceDetails (
27
+ workspaceType,
28
+ workspaceId,
29
+ workspaceKey,
30
+ userApiKey
31
+ ) {
26
32
  /* c8 ignore next 9 */
27
33
  if (!workspaceType) {
28
34
  const answer = await inquirer.prompt({
@@ -38,7 +44,6 @@ async function askWorkspaceDetails (args) {
38
44
  throw new errors.InvalidWorkspaceTypeError(workspaceType, WORKSPACE_TYPES.join(', '))
39
45
  }
40
46
 
41
- let workspaceId = args['workspace-id']
42
47
  /* c8 ignore next 8 */
43
48
  if (!workspaceId) {
44
49
  const answer = await inquirer.prompt({
@@ -53,9 +58,8 @@ async function askWorkspaceDetails (args) {
53
58
  throw new errors.InvalidWorkspaceIdError()
54
59
  }
55
60
 
56
- let workspaceKey = args['workspace-key']
57
61
  /* c8 ignore next 9 */
58
- if (!workspaceKey) {
62
+ if (!workspaceKey && !userApiKey) {
59
63
  const answer = await inquirer.prompt({
60
64
  type: 'password',
61
65
  name: 'workspaceKey',
@@ -72,6 +76,93 @@ async function askWorkspaceDetails (args) {
72
76
  }
73
77
  }
74
78
 
79
+ /* c8 ignore next 26 */
80
+ async function askToChooseApplication (applications) {
81
+ const applicationChoices = applications.map((application) => {
82
+ return {
83
+ name: application.name,
84
+ value: application
85
+ }
86
+ })
87
+ applicationChoices.push({
88
+ name: 'Deploy to another application',
89
+ value: null
90
+ })
91
+
92
+ const answer = await inquirer.prompt({
93
+ type: 'list',
94
+ name: 'application',
95
+ message: 'Select application to deploy:',
96
+ choices: applicationChoices
97
+ })
98
+
99
+ const chosenApplication = answer.application
100
+ if (chosenApplication === null) {
101
+ return null
102
+ }
103
+
104
+ return chosenApplication
105
+ }
106
+
107
+ /* c8 ignore next 27 */
108
+ async function askToChooseWorkspace (workspaces) {
109
+ const workspaceChoices = workspaces.map((workspace) => {
110
+ return {
111
+ name: `${workspace.name} (${workspace.type})`,
112
+ value: workspace
113
+ }
114
+ })
115
+ workspaceChoices.push({
116
+ name: 'Deploy to another workspace',
117
+ value: null
118
+ })
119
+
120
+ const answer = await inquirer.prompt({
121
+ type: 'list',
122
+ name: 'workspace',
123
+ message: 'Select workspace to deploy:',
124
+ choices: workspaceChoices
125
+ })
126
+
127
+ const chosenWorkspace = answer.workspace
128
+ if (chosenWorkspace === null) {
129
+ return null
130
+ }
131
+
132
+ return chosenWorkspace
133
+ }
134
+
135
+ /* c8 ignore next 19 */
136
+ async function askToChooseDeployLabel (labels) {
137
+ const entryPointChoices = labels.map((label) => {
138
+ return { name: label, value: label }
139
+ })
140
+ entryPointChoices.push({
141
+ name: 'Deploy to another label',
142
+ value: null
143
+ })
144
+
145
+ const answer = await inquirer.prompt({
146
+ type: 'list',
147
+ name: 'entryPoint',
148
+ message: 'Select entry point to deploy:',
149
+ choices: entryPointChoices
150
+ })
151
+
152
+ return answer.entryPoint
153
+ }
154
+
155
+ /* c8 ignore next 10 */
156
+ async function askToEnterDeployLabel () {
157
+ const answer = await inquirer.prompt({
158
+ type: 'input',
159
+ name: 'label',
160
+ message: 'Enter deploy label:',
161
+ default: 'cli:deploy-1'
162
+ })
163
+ return answer.label
164
+ }
165
+
75
166
  async function readWorkspaceDetails (workspaceKeysPath) {
76
167
  /* c8 ignore next 3 */
77
168
  if (!isAbsolute(workspaceKeysPath)) {
@@ -106,6 +197,65 @@ async function readWorkspaceDetails (workspaceKeysPath) {
106
197
  throw new errors.CouldNotFindWorkspaceKeysError()
107
198
  }
108
199
 
200
+ /* c8 ignore next 18 */
201
+ async function getUserApplications (deployServiceHost, userApiKey) {
202
+ const { statusCode, body } = await request(`${deployServiceHost}/applications`, {
203
+ headers: {
204
+ 'Content-Type': 'application/json',
205
+ 'x-platformatic-user-api-key': userApiKey
206
+ }
207
+ })
208
+
209
+ if (statusCode !== 200) {
210
+ const error = await body.text()
211
+ console.error(error)
212
+ throw new errors.CouldNotFetchUserApplicationsError()
213
+ }
214
+
215
+ const { applications } = await body.json()
216
+ return applications
217
+ }
218
+
219
+ /* c8 ignore next 18 */
220
+ async function getWorkspaceLabels (deployServiceHost, workspaceId, userApiKey) {
221
+ const { statusCode, body } = await request(`${deployServiceHost}/entrypoints`, {
222
+ headers: {
223
+ 'Content-Type': 'application/json',
224
+ 'x-platformatic-workspace-id': workspaceId,
225
+ 'x-platformatic-user-api-key': userApiKey
226
+ }
227
+ })
228
+
229
+ if (statusCode !== 200) {
230
+ const error = await body.text()
231
+ console.error(error)
232
+ throw new errors.CouldNotFetchDeployLabelsError()
233
+ }
234
+
235
+ const { entryPoints } = await body.json()
236
+ return entryPoints.map((entryPoint) => entryPoint.label)
237
+ }
238
+
239
+ /* c8 ignore next 19 */
240
+ async function getUserWorkspaceDetails (deployServiceHost, userApiKey) {
241
+ const applications = await getUserApplications(deployServiceHost, userApiKey)
242
+ if (applications.length === 0) return null
243
+
244
+ const application = await askToChooseApplication(applications)
245
+ if (application === null) return null
246
+
247
+ const workspaces = application.workspaces
248
+ if (workspaces.length === 0) return null
249
+
250
+ const workspaceDetails = await askToChooseWorkspace(workspaces)
251
+ if (workspaceDetails === null) return null
252
+
253
+ return {
254
+ workspaceType: workspaceDetails.type,
255
+ workspaceId: workspaceDetails.id
256
+ }
257
+ }
258
+
109
259
  export async function deploy (argv) {
110
260
  try {
111
261
  const args = parseArgs(argv, {
@@ -134,24 +284,63 @@ export async function deploy (argv) {
134
284
  }
135
285
  })
136
286
 
137
- const workspaceKeysPath = args.keys
138
- const workspaceDetails = workspaceKeysPath
139
- ? await readWorkspaceDetails(workspaceKeysPath)
140
- : await askWorkspaceDetails(args)
287
+ const deployServiceHost = args['deploy-service-host']
288
+
289
+ let workspaceId = args['workspace-id']
290
+ let workspaceType = args.type
141
291
 
142
- const { workspaceType, workspaceId, workspaceKey } = workspaceDetails
292
+ let workspaceKey = args['workspace-key']
293
+ let userApiKey = null
294
+
295
+ if (!workspaceId) {
296
+ if (args.keys) {
297
+ const workspaceDetails = await readWorkspaceDetails(args.keys)
298
+ workspaceId = workspaceDetails.workspaceId
299
+ workspaceType = workspaceDetails.workspaceType
300
+ workspaceKey = workspaceDetails.workspaceKey
301
+ /* c8 ignore next 10 */
302
+ } else {
303
+ userApiKey = await getUserApiKey()
304
+ if (userApiKey) {
305
+ const workspaceDetails = await getUserWorkspaceDetails(deployServiceHost, userApiKey)
306
+ if (workspaceDetails) {
307
+ workspaceId = workspaceDetails.workspaceId
308
+ workspaceType = workspaceDetails.workspaceType
309
+ }
310
+ }
311
+ }
312
+ }
313
+
314
+ const workspaceDetails = await askMissingWorkspaceDetails(
315
+ workspaceType,
316
+ workspaceId,
317
+ workspaceKey,
318
+ userApiKey
319
+ )
320
+ workspaceId = workspaceDetails.workspaceId
321
+ workspaceType = workspaceDetails.workspaceType
322
+ workspaceKey = workspaceDetails.workspaceKey
143
323
 
144
324
  let label = args.label
325
+
145
326
  if (workspaceType === 'dynamic') {
146
- /* c8 ignore next 9 */
327
+ /* c8 ignore next 17 */
147
328
  if (!label) {
148
- const answer = await inquirer.prompt({
149
- type: 'input',
150
- name: 'label',
151
- message: 'Enter deploy label:',
152
- default: 'cli:deploy-1'
153
- })
154
- label = answer.label
329
+ userApiKey = userApiKey || await getUserApiKey()
330
+
331
+ if (userApiKey) {
332
+ const workspaceLabels = await getWorkspaceLabels(
333
+ deployServiceHost,
334
+ workspaceId,
335
+ userApiKey
336
+ )
337
+ if (workspaceLabels.length !== 0) {
338
+ label = await askToChooseDeployLabel(workspaceLabels)
339
+ }
340
+ }
341
+ if (!label) {
342
+ label = await askToEnterDeployLabel()
343
+ }
155
344
  }
156
345
 
157
346
  const labelPrefix = label.split(':')[0]
@@ -171,12 +360,12 @@ export async function deploy (argv) {
171
360
 
172
361
  const pathToEnvFile = args.env || '.env'
173
362
  const pathToSecretsFile = args.secrets || '.secrets.env'
174
- const deployServiceHost = args['deploy-service-host']
175
363
 
176
364
  await deployClient.deploy({
177
365
  deployServiceHost,
178
366
  workspaceId,
179
367
  workspaceKey,
368
+ userApiKey,
180
369
  pathToProject,
181
370
  pathToConfig,
182
371
  pathToEnvFile,
package/lib/errors.js CHANGED
@@ -7,7 +7,9 @@ const ERROR_PREFIX = 'PLT_CLI'
7
7
  const errors = {
8
8
  InvalidWorkspaceTypeError: createError(`${ERROR_PREFIX}_INVALID_WORKSPACE_TYPE`, 'Invalid workspace type provided: "%s". Type must be one of: %s'),
9
9
  InvalidWorkspaceIdError: createError(`${ERROR_PREFIX}_INVALID_WORKSPACE_ID`, 'Invalid workspace id provided. Workspace id must be a valid uuid.'),
10
- CouldNotFindWorkspaceKeysError: createError(`${ERROR_PREFIX}_COULD_NOT_FIND_WORKSPACE_KEYS`, 'Could not find workspace keys in provided file.')
10
+ CouldNotFindWorkspaceKeysError: createError(`${ERROR_PREFIX}_COULD_NOT_FIND_WORKSPACE_KEYS`, 'Could not find workspace keys in provided file.'),
11
+ CouldNotFetchUserApplicationsError: createError(`${ERROR_PREFIX}_COULD_NOT_FETCH_USER_APPLICATIONS`, 'Could not fetch user applications'),
12
+ CouldNotFetchDeployLabelsError: createError(`${ERROR_PREFIX}_COULD_NOT_FETCH_DEPLOY_LABELS`, 'Could not fetch deploy labels')
11
13
  }
12
14
 
13
15
  export default errors
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "platformatic",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Platformatic CLI",
5
5
  "main": "cli.js",
6
6
  "type": "module",
@@ -49,18 +49,18 @@
49
49
  "pino": "^8.15.3",
50
50
  "pino-pretty": "^10.2.0",
51
51
  "undici": "^5.25.4",
52
- "@platformatic/authenticate": "1.6.0",
53
- "@platformatic/client-cli": "1.6.0",
54
- "@platformatic/composer": "1.6.0",
55
- "@platformatic/config": "1.6.0",
56
- "@platformatic/db": "1.6.0",
57
- "@platformatic/deploy-client": "1.6.0",
58
- "@platformatic/frontend-template": "1.6.0",
59
- "@platformatic/metaconfig": "1.6.0",
60
- "@platformatic/runtime": "1.6.0",
61
- "@platformatic/service": "1.6.0",
62
- "@platformatic/utils": "^1.6.0",
63
- "create-platformatic": "1.6.0"
52
+ "@platformatic/authenticate": "1.7.0",
53
+ "@platformatic/client-cli": "1.7.0",
54
+ "@platformatic/composer": "1.7.0",
55
+ "@platformatic/config": "1.7.0",
56
+ "@platformatic/db": "1.7.0",
57
+ "@platformatic/deploy-client": "1.7.0",
58
+ "@platformatic/frontend-template": "1.7.0",
59
+ "@platformatic/metaconfig": "1.7.0",
60
+ "@platformatic/runtime": "1.7.0",
61
+ "@platformatic/service": "1.7.0",
62
+ "@platformatic/utils": "^1.7.0",
63
+ "create-platformatic": "1.7.0"
64
64
  },
65
65
  "scripts": {
66
66
  "test": "standard | snazzy && c8 node ./test/runner.js",