@gadgetinc/ggt 0.4.9 → 1.0.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.
Files changed (142) hide show
  1. package/README.md +165 -93
  2. package/lib/__generated__/graphql.js +66 -1
  3. package/lib/__generated__/graphql.js.map +1 -1
  4. package/lib/commands/deploy.js +329 -184
  5. package/lib/commands/deploy.js.map +1 -1
  6. package/lib/commands/dev.js +445 -0
  7. package/lib/commands/dev.js.map +1 -0
  8. package/lib/commands/list.js +27 -19
  9. package/lib/commands/list.js.map +1 -1
  10. package/lib/commands/login.js +15 -11
  11. package/lib/commands/login.js.map +1 -1
  12. package/lib/commands/logout.js +5 -5
  13. package/lib/commands/logout.js.map +1 -1
  14. package/lib/commands/open.js +200 -0
  15. package/lib/commands/open.js.map +1 -0
  16. package/lib/commands/pull.js +128 -0
  17. package/lib/commands/pull.js.map +1 -0
  18. package/lib/commands/push.js +126 -0
  19. package/lib/commands/push.js.map +1 -0
  20. package/lib/commands/root.js +46 -28
  21. package/lib/commands/root.js.map +1 -1
  22. package/lib/commands/status.js +61 -0
  23. package/lib/commands/status.js.map +1 -0
  24. package/lib/commands/version.js +6 -6
  25. package/lib/commands/version.js.map +1 -1
  26. package/lib/commands/whoami.js +6 -6
  27. package/lib/commands/whoami.js.map +1 -1
  28. package/lib/ggt.js +33 -8
  29. package/lib/ggt.js.map +1 -1
  30. package/lib/main.js +5 -0
  31. package/lib/main.js.map +1 -0
  32. package/lib/services/app/api/api.js +191 -0
  33. package/lib/services/app/api/api.js.map +1 -0
  34. package/lib/services/app/api/operation.js +12 -0
  35. package/lib/services/app/api/operation.js.map +1 -0
  36. package/lib/services/app/app.js +44 -10
  37. package/lib/services/app/app.js.map +1 -1
  38. package/lib/services/app/{edit/client.js → client.js} +29 -19
  39. package/lib/services/app/client.js.map +1 -0
  40. package/lib/services/app/edit/edit.js +67 -31
  41. package/lib/services/app/edit/edit.js.map +1 -1
  42. package/lib/services/app/edit/operation.js +19 -3
  43. package/lib/services/app/edit/operation.js.map +1 -1
  44. package/lib/services/app/{edit/error.js → error.js} +6 -6
  45. package/lib/services/app/error.js.map +1 -0
  46. package/lib/services/command/arg.js +4 -4
  47. package/lib/services/command/arg.js.map +1 -1
  48. package/lib/services/command/command.js +9 -7
  49. package/lib/services/command/command.js.map +1 -1
  50. package/lib/services/command/context.js +82 -20
  51. package/lib/services/command/context.js.map +1 -1
  52. package/lib/services/config/config.js +4 -7
  53. package/lib/services/config/config.js.map +1 -1
  54. package/lib/services/config/env.js +1 -1
  55. package/lib/services/config/env.js.map +1 -1
  56. package/lib/services/filesync/changes.js +76 -37
  57. package/lib/services/filesync/changes.js.map +1 -1
  58. package/lib/services/filesync/conflicts.js +10 -9
  59. package/lib/services/filesync/conflicts.js.map +1 -1
  60. package/lib/services/filesync/directory.js +16 -1
  61. package/lib/services/filesync/directory.js.map +1 -1
  62. package/lib/services/filesync/error.js +96 -27
  63. package/lib/services/filesync/error.js.map +1 -1
  64. package/lib/services/filesync/filesync.js +448 -472
  65. package/lib/services/filesync/filesync.js.map +1 -1
  66. package/lib/services/filesync/hashes.js +8 -5
  67. package/lib/services/filesync/hashes.js.map +1 -1
  68. package/lib/services/filesync/strategy.js +59 -0
  69. package/lib/services/filesync/strategy.js.map +1 -0
  70. package/lib/services/filesync/sync-json.js +475 -0
  71. package/lib/services/filesync/sync-json.js.map +1 -0
  72. package/lib/services/http/auth.js +30 -1
  73. package/lib/services/http/auth.js.map +1 -1
  74. package/lib/services/http/http.js +5 -0
  75. package/lib/services/http/http.js.map +1 -1
  76. package/lib/services/output/confirm.js +149 -0
  77. package/lib/services/output/confirm.js.map +1 -0
  78. package/lib/services/output/footer.js +22 -0
  79. package/lib/services/output/footer.js.map +1 -0
  80. package/lib/services/output/log/format/pretty.js +2 -1
  81. package/lib/services/output/log/format/pretty.js.map +1 -1
  82. package/lib/services/output/log/logger.js +13 -5
  83. package/lib/services/output/log/logger.js.map +1 -1
  84. package/lib/services/output/log/structured.js +2 -2
  85. package/lib/services/output/log/structured.js.map +1 -1
  86. package/lib/services/output/output.js +197 -0
  87. package/lib/services/output/output.js.map +1 -0
  88. package/lib/services/output/print.js +31 -0
  89. package/lib/services/output/print.js.map +1 -0
  90. package/lib/services/output/problems.js +84 -0
  91. package/lib/services/output/problems.js.map +1 -0
  92. package/lib/services/output/prompt.js +173 -40
  93. package/lib/services/output/prompt.js.map +1 -1
  94. package/lib/services/output/report.js +63 -19
  95. package/lib/services/output/report.js.map +1 -1
  96. package/lib/services/output/select.js +198 -0
  97. package/lib/services/output/select.js.map +1 -0
  98. package/lib/services/output/spinner.js +141 -0
  99. package/lib/services/output/spinner.js.map +1 -0
  100. package/lib/services/output/sprint.js +38 -15
  101. package/lib/services/output/sprint.js.map +1 -1
  102. package/lib/services/output/symbols.js +23 -0
  103. package/lib/services/output/symbols.js.map +1 -0
  104. package/lib/services/output/table.js +98 -0
  105. package/lib/services/output/table.js.map +1 -0
  106. package/lib/services/output/timestamp.js +12 -0
  107. package/lib/services/output/timestamp.js.map +1 -0
  108. package/lib/services/output/update.js +29 -9
  109. package/lib/services/output/update.js.map +1 -1
  110. package/lib/services/user/session.js +4 -0
  111. package/lib/services/user/session.js.map +1 -1
  112. package/lib/services/user/user.js +15 -10
  113. package/lib/services/user/user.js.map +1 -1
  114. package/lib/services/util/assert.js +11 -0
  115. package/lib/services/util/assert.js.map +1 -0
  116. package/lib/services/util/boolean.js +2 -2
  117. package/lib/services/util/boolean.js.map +1 -1
  118. package/lib/services/util/function.js +45 -7
  119. package/lib/services/util/function.js.map +1 -1
  120. package/lib/services/util/is.js +23 -2
  121. package/lib/services/util/is.js.map +1 -1
  122. package/lib/services/util/json.js +16 -13
  123. package/lib/services/util/json.js.map +1 -1
  124. package/lib/services/util/object.js +2 -2
  125. package/lib/services/util/object.js.map +1 -1
  126. package/lib/services/util/promise.js +5 -2
  127. package/lib/services/util/promise.js.map +1 -1
  128. package/lib/services/util/types.js.map +1 -1
  129. package/npm-shrinkwrap.json +3436 -2833
  130. package/package.json +47 -40
  131. package/bin/dev.cmd +0 -3
  132. package/bin/dev.js +0 -14
  133. package/bin/run.cmd +0 -3
  134. package/bin/run.js +0 -5
  135. package/lib/commands/sync.js +0 -284
  136. package/lib/commands/sync.js.map +0 -1
  137. package/lib/services/app/edit/client.js.map +0 -1
  138. package/lib/services/app/edit/error.js.map +0 -1
  139. package/lib/services/output/log/printer.js +0 -120
  140. package/lib/services/output/log/printer.js.map +0 -1
  141. package/lib/services/output/stream.js +0 -54
  142. package/lib/services/output/stream.js.map +0 -1
@@ -1,224 +1,369 @@
1
- import chalk from "chalk";
2
- import ora from "ora";
3
- import { REMOTE_SERVER_CONTRACT_STATUS_SUBSCRIPTION } from "../services/app/edit/operation.js";
4
- import { FileSync, FileSyncArgs } from "../services/filesync/filesync.js";
5
- import { select } from "../services/output/prompt.js";
1
+ import assert from "node:assert";
2
+ import terminalLink from "terminal-link";
3
+ import { PUBLISH_STATUS_SUBSCRIPTION } from "../services/app/edit/operation.js";
4
+ import { DeployDisallowedError } from "../services/filesync/error.js";
5
+ import { FileSync } from "../services/filesync/filesync.js";
6
+ import { SyncJson, loadSyncJsonDirectory } from "../services/filesync/sync-json.js";
7
+ import { confirm } from "../services/output/confirm.js";
8
+ import { output } from "../services/output/output.js";
9
+ import { println } from "../services/output/print.js";
10
+ import { ProblemSeverity, printProblems, publishIssuesToProblems } from "../services/output/problems.js";
11
+ import { reportErrorAndExit } from "../services/output/report.js";
12
+ import { spin } from "../services/output/spinner.js";
6
13
  import { sprint } from "../services/output/sprint.js";
7
- import { isCloseEvent, isGraphQLErrors } from "../services/util/is.js";
8
- export const usage = ()=>sprint`
9
- Deploy your Gadget application's development source code to production.
14
+ import { ts } from "../services/output/timestamp.js";
15
+ import { unreachable } from "../services/util/assert.js";
16
+ import { isGraphQLErrors } from "../services/util/is.js";
17
+ import { args as PushArgs } from "./push.js";
18
+ export const args = {
19
+ ...PushArgs,
20
+ "--env": {
21
+ type: String,
22
+ alias: [
23
+ "-e",
24
+ "--environment",
25
+ "--from"
26
+ ]
27
+ },
28
+ "--allow-problems": {
29
+ type: Boolean,
30
+ alias: "--allow-issues"
31
+ },
32
+ "--allow-charges": {
33
+ type: Boolean
34
+ }
35
+ };
36
+ export const usage = (ctx)=>{
37
+ if (ctx.args["-h"]) {
38
+ return sprint`
39
+ Deploy an environment to production.
40
+
41
+ Your local files must match your environment's files
42
+ before you can deploy. Changes are tracked from
43
+ the last "ggt dev", "ggt push", or "ggt pull" run locally.
44
+
45
+ {bold USAGE}
46
+ ggt deploy
47
+
48
+ {bold EXAMPLES}
49
+ $ ggt deploy
50
+ $ ggt deploy --from=staging
51
+ $ ggt deploy --from=staging --force
52
+ $ ggt deploy --from=staging --force --allow-problems
53
+
54
+ {bold FLAGS}
55
+ -a, --app=<name> The application to deploy
56
+ -e, --from=<env> The environment to deploy from
57
+ --force Discard changes to your environment's filesystem
58
+ --allow-problems Deploy regardless of any problems the environment has
59
+ --allow-charges Deploy even if doing so will add charges to your account
60
+
61
+ Run "ggt deploy --help" for more information.
62
+ `;
63
+ }
64
+ return sprint`
65
+ Deploy an environment to production.
66
+
67
+ Your local files must match your environment's files
68
+ before you can deploy. Changes are tracked from
69
+ the last "ggt dev", "ggt push", or "ggt pull" run locally.
70
+
71
+ If your local files don't match your environment's files, you will
72
+ be prompted to push your local files before you can deploy.
73
+
74
+ If your environment has un-pulled changes, and "--force" is not
75
+ passed, you will be prompted to {underline discard them} or abort the deploy.
10
76
 
11
77
  {bold USAGE}
12
- ggt deploy [DIRECTORY] [--app=<name>]
13
78
 
14
- {bold ARGUMENTS}
15
- DIRECTORY The directory to sync files to and deploy (default: ".")
79
+ ggt deploy [--app=<name>] [--from=<env>] [--force]
80
+ [--allow-problems] [--allow-charges]
81
+
82
+ {bold EXAMPLES}
83
+
84
+ $ ggt deploy
85
+ $ ggt deploy --from=staging
86
+ $ ggt deploy --from=staging --force
87
+ $ ggt deploy --from=staging --force --allow-problems
88
+ $ ggt deploy --from=staging --force --allow-problems --allow-charges
16
89
 
17
90
  {bold FLAGS}
18
- -a, --app=<name> The Gadget application to deploy
19
- --force Deploy the Gadget application regardless of any issues it may have
20
91
 
21
- {bold DESCRIPTION}
22
- Deploy allows you to deploy your current Gadget application in development to production.
92
+ -a, --app, --application=<name>
93
+ The application to deploy.
94
+
95
+ Defaults to the application within the ".gadget/sync.json"
96
+ file in the current directory or any parent directories.
97
+
98
+ -e, --env, --environment, --from=<name>
99
+ The environment to deploy from.
100
+
101
+ Defaults to the environment within the ".gadget/sync.json"
102
+ file in the current directory or any parent directories.
23
103
 
24
- It detects if local files are up to date with remote and if the Gadget application
25
- is in a deployable state. If there are any issues, it will display them and ask if
26
- you would like to deploy anyways.
104
+ -f, --force
105
+ Discard any changes made to your environment's filesystem
106
+ since the last "ggt dev", "ggt push", or "ggt pull".
27
107
 
28
- Note:
29
- • If local files are not up to date or have not recently been synced with remote ones,
30
- you will be prompted to run a one-time sync to ensure the files remain consistent with
31
- what is on the remote.
32
- • You may wish to keep ggt sync running in the background before trying to run ggt deploy
108
+ Defaults to false.
33
109
 
34
- {bold EXAMPLE}
35
- $ ggt deploy ~/gadget/example --app example
110
+ --allow-problems, --allow-issues
111
+ Deploy your environment to production regardless of any problems
112
+ it may have.
36
113
 
37
- App example
38
- Editor https://example.gadget.app/edit
39
- Playground https://example.gadget.app/api/graphql/playground
40
- Docs https://docs.gadget.dev/api/example
114
+ These problems may include:
115
+ • Gelly syntax errors
116
+ • TypeScript errors
117
+ • Models with missing fields
41
118
 
42
- Endpoints
43
- • https://example.gadget.app
44
- • https://example--development.gadget.app
119
+ Defaults to false.
45
120
 
121
+ --allow-charges
122
+ Allows "ggt deploy" to continue when deploying your environment
123
+ to production will add charges to your account.
46
124
 
47
- Building frontend assets ...
48
- ✔ DONE
125
+ Defaults to false.
49
126
 
50
- Setting up database ...
51
- DONE
127
+ --allow-unknown-directory
128
+ Allows "ggt deploy" to continue when the current directory, nor
129
+ any parent directories, contain a ".gadget/sync.json" file
130
+ within it.
52
131
 
53
- Copying development ...
54
- ✔ DONE
132
+ Defaults to false.
55
133
 
56
- Restarting app ...
57
- DONE
134
+ --allow-different-app
135
+ Allows "ggt deploy" to continue with a different "--app" than the
136
+ one found within the ".gadget/sync.json" file.
58
137
 
59
- Deploy completed. Good bye!
138
+ Defaults to false.
139
+
140
+ Run "ggt deploy -h" for less information.
60
141
  `;
61
- export const args = {
62
- ...FileSyncArgs
63
- };
64
- export var Action;
65
- (function(Action) {
66
- Action["DEPLOY_ANYWAYS"] = "Deploy anyways";
67
- Action["SYNC_ONCE"] = "Sync once";
68
- Action["CANCEL"] = "Cancel (Ctrl+C)";
69
- })(Action || (Action = {}));
70
- const AppDeploymentStepsToAppDeployState = (step)=>{
71
- switch(step){
72
- case "NOT_STARTED":
73
- return "Deploy not started";
74
- case "STARTING":
75
- case "BUILDING_ASSETS":
76
- case "UPLOADING_ASSETS":
77
- return "Building frontend assets";
78
- case "CONVERGING_STORAGE":
79
- return "Setting up database";
80
- case "PUBLISHING_TREE":
81
- return "Copying development";
82
- case "RELOADING_SANDBOX":
83
- return "Restarting app";
84
- case "COMPLETED":
85
- return "Deploy completed";
86
- default:
87
- return "Unknown step";
88
- }
89
142
  };
90
- var AppDeploymentSteps;
91
- (function(AppDeploymentSteps) {
92
- AppDeploymentSteps["NOT_STARTED"] = "NOT_STARTED";
93
- AppDeploymentSteps["STARTING"] = "STARTING";
94
- AppDeploymentSteps["BUILDING_ASSETS"] = "BUILDING_ASSETS";
95
- AppDeploymentSteps["UPLOADING_ASSETS"] = "UPLOADING_ASSETS";
96
- AppDeploymentSteps["CONVERGING_STORAGE"] = "CONVERGING_STORAGE";
97
- AppDeploymentSteps["PUBLISHING_TREE"] = "PUBLISHING_TREE";
98
- AppDeploymentSteps["RELOADING_SANDBOX"] = "RELOADING_SANDBOX";
99
- AppDeploymentSteps["COMPLETED"] = "COMPLETED";
100
- })(AppDeploymentSteps || (AppDeploymentSteps = {}));
101
- /**
102
- * Runs the deploy process.
103
- */ export const command = async (ctx, firstRun = true)=>{
104
- const spinner = ora();
105
- let prevProgress = AppDeploymentStepsToAppDeployState("NOT_STARTED");
106
- let action;
107
- // deploy --force != sync --force
108
- const filesync = await FileSync.init(ctx.child({
109
- overwrite: {
110
- "--force": false
111
- }
112
- }));
113
- if (firstRun) {
114
- ctx.log.printlns`App: ${filesync.app.slug}`;
115
- }
116
- const { inSync } = await filesync.hashes();
117
- if (!inSync) {
118
- ctx.log.printlns`
119
- Local files have diverged from remote. Run a sync once to converge your files or keep {italic ggt sync} running in the background.
120
- `;
121
- action = await select(ctx, {
122
- message: "How would you like to proceed?",
123
- choices: [
124
- "Cancel (Ctrl+C)",
125
- "Sync once"
126
- ]
143
+ export const command = async (ctx)=>{
144
+ const directory = await loadSyncJsonDirectory(process.cwd());
145
+ const syncJson = await SyncJson.loadOrInit(ctx, {
146
+ directory
147
+ });
148
+ println({
149
+ ensureEmptyLineAbove: true
150
+ })`
151
+ Deploying ${syncJson.env.name} to ${terminalLink(syncJson.app.primaryDomain, `https://${syncJson.app.primaryDomain}/`)}
152
+ `;
153
+ const filesync = new FileSync(syncJson);
154
+ const hashes = await filesync.hashes(ctx);
155
+ if (!hashes.inSync && (hashes.localChangesToPush.size > 0 || !hashes.onlyDotGadgetFilesChanged)) {
156
+ // the following is true:
157
+ // 1. our local files don't match our environment's files
158
+ // 2. we have local changes to push or non .gadget/ files have changed on our environment
159
+ // therefor, we need to push before we can deploy
160
+ await filesync.print(ctx, {
161
+ hashes
127
162
  });
128
- switch(action){
129
- case "Sync once":
130
- {
131
- await filesync.sync();
163
+ println({
164
+ ensureEmptyLineAbove: true
165
+ })`
166
+ Your environment's files must match your local files before you can deploy.
167
+ `;
168
+ // some scenarios make the confirmation to push changes imply the
169
+ // --force flag (e.g. when both local and environment files have
170
+ // changed, or when only environment files have changed)
171
+ let implicitForce = false;
172
+ if (output.isInteractive) {
173
+ let message;
174
+ switch(true){
175
+ case hashes.bothChanged:
176
+ message = sprint`Would you like to push your local changes and {underline discard your environment's} changes now?`;
177
+ implicitForce = true;
132
178
  break;
133
- }
134
- case "Cancel (Ctrl+C)":
135
- {
136
- process.exit(0);
137
- }
179
+ case hashes.localChangesToPush.size > 0:
180
+ message = sprint`Would you like to push your local changes now?`;
181
+ break;
182
+ case hashes.environmentChanges.size > 0:
183
+ message = sprint`Do you want to {underline discard your environment's} changes now?`;
184
+ implicitForce = true;
185
+ break;
186
+ default:
187
+ unreachable("no changes to push or discard");
188
+ }
189
+ await confirm({
190
+ ensureEmptyLineAbove: true
191
+ })(message);
192
+ } else {
193
+ println({
194
+ ensureEmptyLineAbove: true
195
+ })`
196
+ Assuming you want to push your local files now.
197
+ `;
138
198
  }
199
+ await filesync.push(ctx, {
200
+ hashes,
201
+ force: implicitForce || ctx.args["--force"]
202
+ });
139
203
  }
140
- // subscribes to the graphql subscription that will listen and send back the server contract status
141
- const unsubscribe = filesync.edit.subscribe({
142
- subscription: REMOTE_SERVER_CONTRACT_STATUS_SUBSCRIPTION,
143
- variables: ()=>({
144
- localFilesVersion: String(filesync.filesVersion),
145
- force: ctx.args["--force"]
146
- }),
147
- onError: (error)=>{
148
- if (isCloseEvent(error.cause)) {
149
- spinner.fail("Failed");
150
- ctx.log.printlns(error.message);
151
- } else if (isGraphQLErrors(error.cause)) {
152
- const message = error.cause[0]?.message;
153
- if (message && message.includes("GGT_PAYMENT_REQUIRED")) {
154
- ctx.log.println("Production environment limit reached. Upgrade your plan to deploy");
155
- } else {
156
- ctx.log.println(`${message}`);
157
- }
158
- }
204
+ const variables = {
205
+ localFilesVersion: String(syncJson.filesVersion),
206
+ force: ctx.args["--allow-problems"],
207
+ allowCharges: ctx.args["--allow-charges"]
208
+ };
209
+ let spinner;
210
+ let currentStep = AppDeploymentSteps.NOT_STARTED;
211
+ let printedProblems = false;
212
+ const subscription = syncJson.edit.subscribe({
213
+ subscription: PUBLISH_STATUS_SUBSCRIPTION,
214
+ variables,
215
+ onError: async (error)=>{
159
216
  ctx.log.error("failed to deploy", {
160
217
  error
161
218
  });
162
- unsubscribe();
163
- return;
219
+ spinner?.fail(stepToSpinnerStart(syncJson, currentStep) + " " + ts());
220
+ if (isGraphQLErrors(error.cause)) {
221
+ const graphqlError = error.cause[0];
222
+ assert(graphqlError, "expected graphqlError to be defined");
223
+ switch(true){
224
+ case graphqlError.extensions["requiresUpgrade"]:
225
+ println({
226
+ ensureEmptyLineAbove: true
227
+ })(graphqlError.message.replace(/GGT_PAYMENT_REQUIRED:?\s*/, ""));
228
+ process.exit(1);
229
+ break;
230
+ case graphqlError.extensions["requiresAdditionalCharge"]:
231
+ println({
232
+ ensureEmptyLineAbove: true
233
+ })(graphqlError.message.replace(/GGT_PAYMENT_REQUIRED:?\s*/, ""));
234
+ await confirm({
235
+ ensureEmptyLineAbove: true
236
+ })("Do you want to continue?");
237
+ subscription.resubscribe({
238
+ ...variables,
239
+ allowCharges: true
240
+ });
241
+ return;
242
+ }
243
+ }
244
+ await reportErrorAndExit(ctx, error);
164
245
  },
165
246
  onData: async ({ publishStatus })=>{
166
- const { progress, issues } = publishStatus ?? {};
167
- const hasIssues = issues?.length;
168
- if (firstRun && hasIssues) {
169
- ctx.log.printlns`{underline Issues detected}`;
170
- for (const issue of issues){
171
- const message = issue.message.replace(/"/g, "");
172
- const nodeType = issue.node?.type;
173
- const nodeName = issue.node?.apiIdentifier ?? issue.node?.name;
174
- const nodeParent = issue.node?.parentApiIdentifier;
175
- ctx.log.printlns(`
176
- • ${message}
177
- ${nodeType ? `${nodeType}: ${chalk.cyan(nodeName)}` : ""} ${nodeParent ? `ParentResource: ${chalk.cyan(nodeParent)}` : ""}
178
- `.trim());
247
+ if (!publishStatus) {
248
+ ctx.log.warn("received empty publish status");
249
+ return;
250
+ }
251
+ const { publishStarted, progress: step, issues, status } = publishStatus;
252
+ if (!printedProblems && issues.length > 0) {
253
+ printedProblems = true;
254
+ const fatalIssues = issues.filter((issue)=>issue.severity === ProblemSeverity.Fatal);
255
+ if (fatalIssues.length > 0) {
256
+ await reportErrorAndExit(ctx, new DeployDisallowedError(publishIssuesToProblems(fatalIssues)));
179
257
  }
180
- if (!ctx.args["--force"]) {
181
- unsubscribe();
182
- action = await select(ctx, {
183
- message: "Detected some issues with your app. How would you like to proceed?",
184
- choices: [
185
- "Cancel (Ctrl+C)",
186
- "Deploy anyways"
187
- ]
258
+ println({
259
+ ensureEmptyLineAbove: true
260
+ })`{bold Problems found.}`;
261
+ printProblems({
262
+ problems: publishIssuesToProblems(issues)
263
+ });
264
+ if (!publishStarted) {
265
+ await confirm("Do you want to continue?");
266
+ subscription.resubscribe({
267
+ ...variables,
268
+ force: true
188
269
  });
189
- switch(action){
190
- case "Deploy anyways":
191
- {
192
- ctx.args["--force"] = true;
193
- await command(ctx, false);
194
- break;
195
- }
196
- case "Cancel (Ctrl+C)":
197
- {
198
- process.exit(0);
199
- }
200
- }
270
+ } else {
271
+ assert(ctx.args["--allow-problems"], "expected --allow-problems to be true");
272
+ println({
273
+ ensureEmptyLineAbove: true
274
+ })`Deploying regardless of problems because {bold "--allow-problems"} was passed.`;
201
275
  }
202
- firstRun = false;
203
- } else {
204
- if (progress === "COMPLETED") {
205
- spinner.succeed("DONE");
206
- ctx.log.printlns("Deploy completed. Good bye!");
207
- unsubscribe();
208
- return;
276
+ return;
277
+ }
278
+ if (status?.code === "Errored") {
279
+ subscription.unsubscribe();
280
+ spinner?.fail(stepToSpinnerStart(syncJson, currentStep) + " " + ts());
281
+ if (status.message) {
282
+ println({
283
+ ensureEmptyLineAbove: true
284
+ })`{red ${status.message}}`;
285
+ }
286
+ if (status.output) {
287
+ println({
288
+ ensureEmptyLineAbove: true
289
+ })`${terminalLink("Check logs", status.output)}`;
209
290
  }
210
- const currentProgress = AppDeploymentStepsToAppDeployState(progress);
211
- if (progress && currentProgress !== prevProgress) {
212
- if (progress !== "STARTING") {
213
- spinner.succeed("DONE");
214
- }
215
- prevProgress = currentProgress;
216
- ctx.log.printlns(`${currentProgress} ...`);
217
- spinner.start("Working ...");
291
+ return;
292
+ }
293
+ if (step === AppDeploymentSteps.COMPLETED) {
294
+ subscription.unsubscribe();
295
+ spinner?.succeed(stepToSpinnerEnd(syncJson, currentStep));
296
+ let message = sprint`{green Deploy successful!}`;
297
+ if (status?.output) {
298
+ message += ` ${terminalLink("Check logs", status.output)}.`;
218
299
  }
300
+ println({
301
+ ensureEmptyLineAbove: true
302
+ })(message);
303
+ return;
304
+ }
305
+ if (step !== currentStep) {
306
+ const spinnerText = stepToSpinnerStart(syncJson, step);
307
+ if (spinnerText !== spinner?.text) {
308
+ // stop the current spinner, if any, and start a new one
309
+ spinner?.succeed(stepToSpinnerEnd(syncJson, currentStep));
310
+ const ensureEmptyLineAbove = currentStep === AppDeploymentSteps.NOT_STARTED || !output.isInteractive;
311
+ spinner = spin({
312
+ ensureEmptyLineAbove
313
+ })(spinnerText);
314
+ }
315
+ currentStep = step;
219
316
  }
220
317
  }
221
318
  });
222
319
  };
320
+ export const AppDeploymentSteps = Object.freeze({
321
+ NOT_STARTED: "NOT_STARTED",
322
+ STARTING: "STARTING",
323
+ BUILDING_ASSETS: "BUILDING_ASSETS",
324
+ UPLOADING_ASSETS: "UPLOADING_ASSETS",
325
+ CONVERGING_STORAGE: "CONVERGING_STORAGE",
326
+ PUBLISHING_TREE: "PUBLISHING_TREE",
327
+ RELOADING_SANDBOX: "RELOADING_SANDBOX",
328
+ COMPLETED: "COMPLETED"
329
+ });
330
+ export const stepToSpinnerStart = (syncJson, step)=>{
331
+ switch(step){
332
+ case AppDeploymentSteps.NOT_STARTED:
333
+ case AppDeploymentSteps.STARTING:
334
+ case AppDeploymentSteps.BUILDING_ASSETS:
335
+ case AppDeploymentSteps.UPLOADING_ASSETS:
336
+ return "Building frontend assets.";
337
+ case AppDeploymentSteps.CONVERGING_STORAGE:
338
+ return "Setting up database.";
339
+ case AppDeploymentSteps.PUBLISHING_TREE:
340
+ return `Copying ${syncJson.env.name}.`;
341
+ case AppDeploymentSteps.RELOADING_SANDBOX:
342
+ return "Restarting app.";
343
+ case AppDeploymentSteps.COMPLETED:
344
+ return "Deploy complete!";
345
+ default:
346
+ return "Unknown step.";
347
+ }
348
+ };
349
+ export const stepToSpinnerEnd = (syncJson, step)=>{
350
+ switch(step){
351
+ case AppDeploymentSteps.NOT_STARTED:
352
+ case AppDeploymentSteps.STARTING:
353
+ case AppDeploymentSteps.BUILDING_ASSETS:
354
+ case AppDeploymentSteps.UPLOADING_ASSETS:
355
+ return `Built frontend assets. ${ts()}`;
356
+ case AppDeploymentSteps.CONVERGING_STORAGE:
357
+ return `Setup database. ${ts()}`;
358
+ case AppDeploymentSteps.PUBLISHING_TREE:
359
+ return `Copied ${syncJson.env.name}. ${ts()}`;
360
+ case AppDeploymentSteps.RELOADING_SANDBOX:
361
+ return `Restarted app. ${ts()}`;
362
+ case AppDeploymentSteps.COMPLETED:
363
+ return "Deploy successful!";
364
+ default:
365
+ return `Completed unknown step. ${ts()}`;
366
+ }
367
+ };
223
368
 
224
369
  //# sourceMappingURL=deploy.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/deploy.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport ora from \"ora\";\nimport { REMOTE_SERVER_CONTRACT_STATUS_SUBSCRIPTION } from \"../services/app/edit/operation.js\";\nimport type { ArgsDefinition } from \"../services/command/arg.js\";\nimport { type Command, type Usage } from \"../services/command/command.js\";\nimport { FileSync, FileSyncArgs } from \"../services/filesync/filesync.js\";\nimport { select } from \"../services/output/prompt.js\";\nimport { sprint } from \"../services/output/sprint.js\";\nimport { isCloseEvent, isGraphQLErrors } from \"../services/util/is.js\";\n\nexport const usage: Usage = () => sprint`\n Deploy your Gadget application's development source code to production.\n\n {bold USAGE}\n ggt deploy [DIRECTORY] [--app=<name>]\n\n {bold ARGUMENTS}\n DIRECTORY The directory to sync files to and deploy (default: \".\")\n\n {bold FLAGS}\n -a, --app=<name> The Gadget application to deploy\n --force Deploy the Gadget application regardless of any issues it may have\n\n {bold DESCRIPTION}\n Deploy allows you to deploy your current Gadget application in development to production.\n\n It detects if local files are up to date with remote and if the Gadget application\n is in a deployable state. If there are any issues, it will display them and ask if\n you would like to deploy anyways.\n\n Note:\n • If local files are not up to date or have not recently been synced with remote ones,\n you will be prompted to run a one-time sync to ensure the files remain consistent with\n what is on the remote.\n • You may wish to keep ggt sync running in the background before trying to run ggt deploy\n\n {bold EXAMPLE}\n $ ggt deploy ~/gadget/example --app example\n\n App example\n Editor https://example.gadget.app/edit\n Playground https://example.gadget.app/api/graphql/playground\n Docs https://docs.gadget.dev/api/example\n\n Endpoints\n • https://example.gadget.app\n • https://example--development.gadget.app\n\n\n Building frontend assets ...\n ✔ DONE\n\n Setting up database ...\n ✔ DONE\n\n Copying development ...\n ✔ DONE\n\n Restarting app ...\n ✔ DONE\n\n Deploy completed. Good bye!\n`;\n\nexport const args = {\n ...FileSyncArgs,\n} satisfies ArgsDefinition;\n\nexport enum Action {\n DEPLOY_ANYWAYS = \"Deploy anyways\",\n SYNC_ONCE = \"Sync once\",\n CANCEL = \"Cancel (Ctrl+C)\",\n}\n\nconst AppDeploymentStepsToAppDeployState = (step: string | undefined): string => {\n switch (step) {\n case \"NOT_STARTED\":\n return \"Deploy not started\";\n case \"STARTING\":\n case \"BUILDING_ASSETS\":\n case \"UPLOADING_ASSETS\":\n return \"Building frontend assets\";\n case \"CONVERGING_STORAGE\":\n return \"Setting up database\";\n case \"PUBLISHING_TREE\":\n return \"Copying development\";\n case \"RELOADING_SANDBOX\":\n return \"Restarting app\";\n case \"COMPLETED\":\n return \"Deploy completed\";\n default:\n return \"Unknown step\";\n }\n};\n\nenum AppDeploymentSteps {\n NOT_STARTED = \"NOT_STARTED\",\n STARTING = \"STARTING\",\n BUILDING_ASSETS = \"BUILDING_ASSETS\",\n UPLOADING_ASSETS = \"UPLOADING_ASSETS\",\n CONVERGING_STORAGE = \"CONVERGING_STORAGE\",\n PUBLISHING_TREE = \"PUBLISHING_TREE\",\n RELOADING_SANDBOX = \"RELOADING_SANDBOX\",\n COMPLETED = \"COMPLETED\",\n}\n\n/**\n * Runs the deploy process.\n */\nexport const command = (async (ctx, firstRun = true) => {\n const spinner = ora();\n let prevProgress: string | undefined = AppDeploymentStepsToAppDeployState(\"NOT_STARTED\");\n let action: Action;\n\n // deploy --force != sync --force\n const filesync = await FileSync.init(ctx.child({ overwrite: { \"--force\": false } }));\n\n if (firstRun) {\n ctx.log.printlns`App: ${filesync.app.slug}`;\n }\n\n const { inSync } = await filesync.hashes();\n if (!inSync) {\n ctx.log.printlns`\n Local files have diverged from remote. Run a sync once to converge your files or keep {italic ggt sync} running in the background.\n `;\n\n action = await select(ctx, {\n message: \"How would you like to proceed?\",\n choices: [Action.CANCEL, Action.SYNC_ONCE],\n });\n\n switch (action) {\n case Action.SYNC_ONCE: {\n await filesync.sync();\n\n break;\n }\n case Action.CANCEL: {\n process.exit(0);\n }\n }\n }\n\n // subscribes to the graphql subscription that will listen and send back the server contract status\n const unsubscribe = filesync.edit.subscribe({\n subscription: REMOTE_SERVER_CONTRACT_STATUS_SUBSCRIPTION,\n variables: () => ({ localFilesVersion: String(filesync.filesVersion), force: ctx.args[\"--force\"] }),\n onError: (error) => {\n if (isCloseEvent(error.cause)) {\n spinner.fail(\"Failed\");\n ctx.log.printlns(error.message);\n } else if (isGraphQLErrors(error.cause)) {\n const message = error.cause[0]?.message;\n if (message && message.includes(\"GGT_PAYMENT_REQUIRED\")) {\n ctx.log.println(\"Production environment limit reached. Upgrade your plan to deploy\");\n } else {\n ctx.log.println(`${message}`);\n }\n }\n ctx.log.error(\"failed to deploy\", { error });\n unsubscribe();\n return;\n },\n onData: async ({ publishStatus }): Promise<void> => {\n const { progress, issues } = publishStatus ?? {};\n\n const hasIssues = issues?.length;\n\n if (firstRun && hasIssues) {\n ctx.log.printlns`{underline Issues detected}`;\n\n for (const issue of issues) {\n const message = issue.message.replace(/\"/g, \"\");\n const nodeType = issue.node?.type;\n const nodeName = issue.node?.apiIdentifier ?? issue.node?.name;\n const nodeParent = issue.node?.parentApiIdentifier;\n\n ctx.log.printlns(\n `\n • ${message}\n ${nodeType ? `${nodeType}: ${chalk.cyan(nodeName)}` : \"\"} ${\n nodeParent ? `ParentResource: ${chalk.cyan(nodeParent)}` : \"\"\n }\n `.trim(),\n );\n }\n\n if (!ctx.args[\"--force\"]) {\n unsubscribe();\n\n action = await select(ctx, {\n message: \"Detected some issues with your app. How would you like to proceed?\",\n choices: [Action.CANCEL, Action.DEPLOY_ANYWAYS],\n });\n\n switch (action) {\n case Action.DEPLOY_ANYWAYS: {\n ctx.args[\"--force\"] = true;\n await command(ctx, false);\n break;\n }\n case Action.CANCEL: {\n process.exit(0);\n }\n }\n }\n\n firstRun = false;\n } else {\n if (progress === AppDeploymentSteps.COMPLETED) {\n spinner.succeed(\"DONE\");\n ctx.log.printlns(\"Deploy completed. Good bye!\");\n unsubscribe();\n return;\n }\n\n const currentProgress = AppDeploymentStepsToAppDeployState(progress);\n\n if (progress && currentProgress !== prevProgress) {\n if ((progress as AppDeploymentSteps) !== AppDeploymentSteps.STARTING) {\n spinner.succeed(\"DONE\");\n }\n\n prevProgress = currentProgress;\n ctx.log.printlns(`${currentProgress} ...`);\n spinner.start(\"Working ...\");\n }\n }\n },\n });\n}) satisfies Command<typeof args>;\n"],"names":["chalk","ora","REMOTE_SERVER_CONTRACT_STATUS_SUBSCRIPTION","FileSync","FileSyncArgs","select","sprint","isCloseEvent","isGraphQLErrors","usage","args","Action","AppDeploymentStepsToAppDeployState","step","AppDeploymentSteps","command","ctx","firstRun","spinner","prevProgress","action","filesync","init","child","overwrite","log","printlns","app","slug","inSync","hashes","message","choices","sync","process","exit","unsubscribe","edit","subscribe","subscription","variables","localFilesVersion","String","filesVersion","force","onError","error","cause","fail","includes","println","onData","publishStatus","progress","issues","hasIssues","length","issue","replace","nodeType","node","type","nodeName","apiIdentifier","name","nodeParent","parentApiIdentifier","cyan","trim","succeed","currentProgress","start"],"mappings":"AAAA,OAAOA,WAAW,QAAQ;AAC1B,OAAOC,SAAS,MAAM;AACtB,SAASC,0CAA0C,QAAQ,oCAAoC;AAG/F,SAASC,QAAQ,EAAEC,YAAY,QAAQ,mCAAmC;AAC1E,SAASC,MAAM,QAAQ,+BAA+B;AACtD,SAASC,MAAM,QAAQ,+BAA+B;AACtD,SAASC,YAAY,EAAEC,eAAe,QAAQ,yBAAyB;AAEvE,OAAO,MAAMC,QAAe,IAAMH,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDzC,CAAC,CAAC;AAEF,OAAO,MAAMI,OAAO;IAClB,GAAGN,YAAY;AACjB,EAA2B;;UAEfO;;;;GAAAA,WAAAA;AAMZ,MAAMC,qCAAqC,CAACC;IAC1C,OAAQA;QACN,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;QACL,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT;YACE,OAAO;IACX;AACF;;UAEKC;;;;;;;;;GAAAA,uBAAAA;AAWL;;CAEC,GACD,OAAO,MAAMC,UAAW,OAAOC,KAAKC,WAAW,IAAI;IACjD,MAAMC,UAAUjB;IAChB,IAAIkB,eAAmCP,mCAAmC;IAC1E,IAAIQ;IAEJ,iCAAiC;IACjC,MAAMC,WAAW,MAAMlB,SAASmB,IAAI,CAACN,IAAIO,KAAK,CAAC;QAAEC,WAAW;YAAE,WAAW;QAAM;IAAE;IAEjF,IAAIP,UAAU;QACZD,IAAIS,GAAG,CAACC,QAAQ,CAAC,KAAK,EAAEL,SAASM,GAAG,CAACC,IAAI,CAAC,CAAC;IAC7C;IAEA,MAAM,EAAEC,MAAM,EAAE,GAAG,MAAMR,SAASS,MAAM;IACxC,IAAI,CAACD,QAAQ;QACXb,IAAIS,GAAG,CAACC,QAAQ,CAAC;;IAEjB,CAAC;QAEDN,SAAS,MAAMf,OAAOW,KAAK;YACzBe,SAAS;YACTC,SAAS;;;aAAiC;QAC5C;QAEA,OAAQZ;YACN;gBAAuB;oBACrB,MAAMC,SAASY,IAAI;oBAEnB;gBACF;YACA;gBAAoB;oBAClBC,QAAQC,IAAI,CAAC;gBACf;QACF;IACF;IAEA,mGAAmG;IACnG,MAAMC,cAAcf,SAASgB,IAAI,CAACC,SAAS,CAAC;QAC1CC,cAAcrC;QACdsC,WAAW,IAAO,CAAA;gBAAEC,mBAAmBC,OAAOrB,SAASsB,YAAY;gBAAGC,OAAO5B,IAAIN,IAAI,CAAC,UAAU;YAAC,CAAA;QACjGmC,SAAS,CAACC;YACR,IAAIvC,aAAauC,MAAMC,KAAK,GAAG;gBAC7B7B,QAAQ8B,IAAI,CAAC;gBACbhC,IAAIS,GAAG,CAACC,QAAQ,CAACoB,MAAMf,OAAO;YAChC,OAAO,IAAIvB,gBAAgBsC,MAAMC,KAAK,GAAG;gBACvC,MAAMhB,UAAUe,MAAMC,KAAK,CAAC,EAAE,EAAEhB;gBAChC,IAAIA,WAAWA,QAAQkB,QAAQ,CAAC,yBAAyB;oBACvDjC,IAAIS,GAAG,CAACyB,OAAO,CAAC;gBAClB,OAAO;oBACLlC,IAAIS,GAAG,CAACyB,OAAO,CAAC,CAAC,EAAEnB,QAAQ,CAAC;gBAC9B;YACF;YACAf,IAAIS,GAAG,CAACqB,KAAK,CAAC,oBAAoB;gBAAEA;YAAM;YAC1CV;YACA;QACF;QACAe,QAAQ,OAAO,EAAEC,aAAa,EAAE;YAC9B,MAAM,EAAEC,QAAQ,EAAEC,MAAM,EAAE,GAAGF,iBAAiB,CAAC;YAE/C,MAAMG,YAAYD,QAAQE;YAE1B,IAAIvC,YAAYsC,WAAW;gBACzBvC,IAAIS,GAAG,CAACC,QAAQ,CAAC,2BAA2B,CAAC;gBAE7C,KAAK,MAAM+B,SAASH,OAAQ;oBAC1B,MAAMvB,UAAU0B,MAAM1B,OAAO,CAAC2B,OAAO,CAAC,MAAM;oBAC5C,MAAMC,WAAWF,MAAMG,IAAI,EAAEC;oBAC7B,MAAMC,WAAWL,MAAMG,IAAI,EAAEG,iBAAiBN,MAAMG,IAAI,EAAEI;oBAC1D,MAAMC,aAAaR,MAAMG,IAAI,EAAEM;oBAE/BlD,IAAIS,GAAG,CAACC,QAAQ,CACd,CAAC;sBACS,EAAEK,QAAQ;sBACV,EAAE4B,WAAW,CAAC,EAAEA,SAAS,EAAE,EAAE3D,MAAMmE,IAAI,CAACL,UAAU,CAAC,GAAG,GAAG,iBAAiB,EACxEG,aAAa,CAAC,gBAAgB,EAAEjE,MAAMmE,IAAI,CAACF,YAAY,CAAC,GAAG,GAC5D;YACX,CAAC,CAACG,IAAI;gBAEV;gBAEA,IAAI,CAACpD,IAAIN,IAAI,CAAC,UAAU,EAAE;oBACxB0B;oBAEAhB,SAAS,MAAMf,OAAOW,KAAK;wBACzBe,SAAS;wBACTC,SAAS;;;yBAAsC;oBACjD;oBAEA,OAAQZ;wBACN;4BAA4B;gCAC1BJ,IAAIN,IAAI,CAAC,UAAU,GAAG;gCACtB,MAAMK,QAAQC,KAAK;gCACnB;4BACF;wBACA;4BAAoB;gCAClBkB,QAAQC,IAAI,CAAC;4BACf;oBACF;gBACF;gBAEAlB,WAAW;YACb,OAAO;gBACL,IAAIoC,0BAA2C;oBAC7CnC,QAAQmD,OAAO,CAAC;oBAChBrD,IAAIS,GAAG,CAACC,QAAQ,CAAC;oBACjBU;oBACA;gBACF;gBAEA,MAAMkC,kBAAkB1D,mCAAmCyC;gBAE3D,IAAIA,YAAYiB,oBAAoBnD,cAAc;oBAChD,IAAI,AAACkC,yBAAiE;wBACpEnC,QAAQmD,OAAO,CAAC;oBAClB;oBAEAlD,eAAemD;oBACftD,IAAIS,GAAG,CAACC,QAAQ,CAAC,CAAC,EAAE4C,gBAAgB,IAAI,CAAC;oBACzCpD,QAAQqD,KAAK,CAAC;gBAChB;YACF;QACF;IACF;AACF,EAAkC"}
1
+ {"version":3,"sources":["../../src/commands/deploy.ts"],"sourcesContent":["import assert from \"node:assert\";\nimport terminalLink from \"terminal-link\";\nimport { PUBLISH_STATUS_SUBSCRIPTION } from \"../services/app/edit/operation.js\";\nimport { type Command, type Usage } from \"../services/command/command.js\";\nimport { DeployDisallowedError } from \"../services/filesync/error.js\";\nimport { FileSync } from \"../services/filesync/filesync.js\";\nimport { SyncJson, loadSyncJsonDirectory } from \"../services/filesync/sync-json.js\";\nimport { confirm } from \"../services/output/confirm.js\";\nimport { output } from \"../services/output/output.js\";\nimport { println } from \"../services/output/print.js\";\nimport { ProblemSeverity, printProblems, publishIssuesToProblems } from \"../services/output/problems.js\";\nimport { reportErrorAndExit } from \"../services/output/report.js\";\nimport { spin, type spinner } from \"../services/output/spinner.js\";\nimport { sprint } from \"../services/output/sprint.js\";\nimport { ts } from \"../services/output/timestamp.js\";\nimport { unreachable } from \"../services/util/assert.js\";\nimport { isGraphQLErrors } from \"../services/util/is.js\";\nimport { args as PushArgs } from \"./push.js\";\n\nexport type DeployArgs = typeof args;\n\nexport const args = {\n ...PushArgs,\n \"--env\": { type: String, alias: [\"-e\", \"--environment\", \"--from\"] },\n \"--allow-problems\": { type: Boolean, alias: \"--allow-issues\" },\n \"--allow-charges\": { type: Boolean },\n};\n\nexport const usage: Usage = (ctx) => {\n if (ctx.args[\"-h\"]) {\n return sprint`\n Deploy an environment to production.\n\n Your local files must match your environment's files\n before you can deploy. Changes are tracked from\n the last \"ggt dev\", \"ggt push\", or \"ggt pull\" run locally.\n\n {bold USAGE}\n ggt deploy\n\n {bold EXAMPLES}\n $ ggt deploy\n $ ggt deploy --from=staging\n $ ggt deploy --from=staging --force\n $ ggt deploy --from=staging --force --allow-problems\n\n {bold FLAGS}\n -a, --app=<name> The application to deploy\n -e, --from=<env> The environment to deploy from\n --force Discard changes to your environment's filesystem\n --allow-problems Deploy regardless of any problems the environment has\n --allow-charges Deploy even if doing so will add charges to your account\n\n Run \"ggt deploy --help\" for more information.\n `;\n }\n\n return sprint`\n Deploy an environment to production.\n\n Your local files must match your environment's files\n before you can deploy. Changes are tracked from\n the last \"ggt dev\", \"ggt push\", or \"ggt pull\" run locally.\n\n If your local files don't match your environment's files, you will\n be prompted to push your local files before you can deploy.\n\n If your environment has un-pulled changes, and \"--force\" is not\n passed, you will be prompted to {underline discard them} or abort the deploy.\n\n {bold USAGE}\n\n ggt deploy [--app=<name>] [--from=<env>] [--force]\n [--allow-problems] [--allow-charges]\n\n {bold EXAMPLES}\n\n $ ggt deploy\n $ ggt deploy --from=staging\n $ ggt deploy --from=staging --force\n $ ggt deploy --from=staging --force --allow-problems\n $ ggt deploy --from=staging --force --allow-problems --allow-charges\n\n {bold FLAGS}\n\n -a, --app, --application=<name>\n The application to deploy.\n\n Defaults to the application within the \".gadget/sync.json\"\n file in the current directory or any parent directories.\n\n -e, --env, --environment, --from=<name>\n The environment to deploy from.\n\n Defaults to the environment within the \".gadget/sync.json\"\n file in the current directory or any parent directories.\n\n -f, --force\n Discard any changes made to your environment's filesystem\n since the last \"ggt dev\", \"ggt push\", or \"ggt pull\".\n\n Defaults to false.\n\n --allow-problems, --allow-issues\n Deploy your environment to production regardless of any problems\n it may have.\n\n These problems may include:\n • Gelly syntax errors\n • TypeScript errors\n • Models with missing fields\n\n Defaults to false.\n\n --allow-charges\n Allows \"ggt deploy\" to continue when deploying your environment\n to production will add charges to your account.\n\n Defaults to false.\n\n --allow-unknown-directory\n Allows \"ggt deploy\" to continue when the current directory, nor\n any parent directories, contain a \".gadget/sync.json\" file\n within it.\n\n Defaults to false.\n\n --allow-different-app\n Allows \"ggt deploy\" to continue with a different \"--app\" than the\n one found within the \".gadget/sync.json\" file.\n\n Defaults to false.\n\n Run \"ggt deploy -h\" for less information.\n`;\n};\n\nexport const command: Command<DeployArgs> = async (ctx) => {\n const directory = await loadSyncJsonDirectory(process.cwd());\n const syncJson = await SyncJson.loadOrInit(ctx, { directory });\n\n println({ ensureEmptyLineAbove: true })`\n Deploying ${syncJson.env.name} to ${terminalLink(syncJson.app.primaryDomain, `https://${syncJson.app.primaryDomain}/`)}\n `;\n\n const filesync = new FileSync(syncJson);\n const hashes = await filesync.hashes(ctx);\n if (!hashes.inSync && (hashes.localChangesToPush.size > 0 || !hashes.onlyDotGadgetFilesChanged)) {\n // the following is true:\n // 1. our local files don't match our environment's files\n // 2. we have local changes to push or non .gadget/ files have changed on our environment\n // therefor, we need to push before we can deploy\n await filesync.print(ctx, { hashes });\n\n println({ ensureEmptyLineAbove: true })`\n Your environment's files must match your local files before you can deploy.\n `;\n\n // some scenarios make the confirmation to push changes imply the\n // --force flag (e.g. when both local and environment files have\n // changed, or when only environment files have changed)\n let implicitForce = false;\n\n if (output.isInteractive) {\n let message: string;\n switch (true) {\n case hashes.bothChanged:\n message = sprint`Would you like to push your local changes and {underline discard your environment's} changes now?`;\n implicitForce = true;\n break;\n case hashes.localChangesToPush.size > 0:\n message = sprint`Would you like to push your local changes now?`;\n break;\n case hashes.environmentChanges.size > 0:\n message = sprint`Do you want to {underline discard your environment's} changes now?`;\n implicitForce = true;\n break;\n default:\n unreachable(\"no changes to push or discard\");\n }\n\n await confirm({ ensureEmptyLineAbove: true })(message);\n } else {\n println({ ensureEmptyLineAbove: true })`\n Assuming you want to push your local files now.\n `;\n }\n\n await filesync.push(ctx, { hashes, force: implicitForce || ctx.args[\"--force\"] });\n }\n\n const variables = {\n localFilesVersion: String(syncJson.filesVersion),\n force: ctx.args[\"--allow-problems\"],\n allowCharges: ctx.args[\"--allow-charges\"],\n };\n\n let spinner: spinner | undefined;\n let currentStep: AppDeploymentSteps = AppDeploymentSteps.NOT_STARTED;\n let printedProblems = false;\n\n const subscription = syncJson.edit.subscribe({\n subscription: PUBLISH_STATUS_SUBSCRIPTION,\n variables,\n onError: async (error) => {\n ctx.log.error(\"failed to deploy\", { error });\n spinner?.fail(stepToSpinnerStart(syncJson, currentStep) + \" \" + ts());\n\n if (isGraphQLErrors(error.cause)) {\n const graphqlError = error.cause[0];\n assert(graphqlError, \"expected graphqlError to be defined\");\n\n switch (true) {\n case graphqlError.extensions[\"requiresUpgrade\"]:\n println({ ensureEmptyLineAbove: true })(graphqlError.message.replace(/GGT_PAYMENT_REQUIRED:?\\s*/, \"\"));\n process.exit(1);\n break;\n case graphqlError.extensions[\"requiresAdditionalCharge\"]:\n println({ ensureEmptyLineAbove: true })(graphqlError.message.replace(/GGT_PAYMENT_REQUIRED:?\\s*/, \"\"));\n await confirm({ ensureEmptyLineAbove: true })(\"Do you want to continue?\");\n subscription.resubscribe({ ...variables, allowCharges: true });\n return;\n }\n }\n\n await reportErrorAndExit(ctx, error);\n },\n onData: async ({ publishStatus }): Promise<void> => {\n if (!publishStatus) {\n ctx.log.warn(\"received empty publish status\");\n return;\n }\n\n const { publishStarted, progress: step, issues, status } = publishStatus;\n if (!printedProblems && issues.length > 0) {\n printedProblems = true;\n\n const fatalIssues = issues.filter((issue) => issue.severity === ProblemSeverity.Fatal);\n if (fatalIssues.length > 0) {\n await reportErrorAndExit(ctx, new DeployDisallowedError(publishIssuesToProblems(fatalIssues)));\n }\n\n println({ ensureEmptyLineAbove: true })`{bold Problems found.}`;\n printProblems({ problems: publishIssuesToProblems(issues) });\n\n if (!publishStarted) {\n await confirm(\"Do you want to continue?\");\n subscription.resubscribe({ ...variables, force: true });\n } else {\n assert(ctx.args[\"--allow-problems\"], \"expected --allow-problems to be true\");\n println({ ensureEmptyLineAbove: true })`Deploying regardless of problems because {bold \"--allow-problems\"} was passed.`;\n }\n\n return;\n }\n\n if (status?.code === \"Errored\") {\n subscription.unsubscribe();\n spinner?.fail(stepToSpinnerStart(syncJson, currentStep) + \" \" + ts());\n\n if (status.message) {\n println({ ensureEmptyLineAbove: true })`{red ${status.message}}`;\n }\n if (status.output) {\n println({ ensureEmptyLineAbove: true })`${terminalLink(\"Check logs\", status.output)}`;\n }\n return;\n }\n\n if (step === AppDeploymentSteps.COMPLETED) {\n subscription.unsubscribe();\n spinner?.succeed(stepToSpinnerEnd(syncJson, currentStep));\n\n let message = sprint`{green Deploy successful!}`;\n if (status?.output) {\n message += ` ${terminalLink(\"Check logs\", status.output)}.`;\n }\n\n println({ ensureEmptyLineAbove: true })(message);\n return;\n }\n\n if (step !== currentStep) {\n const spinnerText = stepToSpinnerStart(syncJson, step);\n if (spinnerText !== spinner?.text) {\n // stop the current spinner, if any, and start a new one\n spinner?.succeed(stepToSpinnerEnd(syncJson, currentStep));\n\n const ensureEmptyLineAbove = currentStep === AppDeploymentSteps.NOT_STARTED || !output.isInteractive;\n spinner = spin({ ensureEmptyLineAbove })(spinnerText);\n }\n\n currentStep = step as AppDeploymentSteps;\n }\n },\n });\n};\n\nexport const AppDeploymentSteps = Object.freeze({\n NOT_STARTED: \"NOT_STARTED\",\n STARTING: \"STARTING\",\n BUILDING_ASSETS: \"BUILDING_ASSETS\",\n UPLOADING_ASSETS: \"UPLOADING_ASSETS\",\n CONVERGING_STORAGE: \"CONVERGING_STORAGE\",\n PUBLISHING_TREE: \"PUBLISHING_TREE\",\n RELOADING_SANDBOX: \"RELOADING_SANDBOX\",\n COMPLETED: \"COMPLETED\",\n});\n\nexport type AppDeploymentSteps = (typeof AppDeploymentSteps)[keyof typeof AppDeploymentSteps];\n\nexport const stepToSpinnerStart = (syncJson: SyncJson, step: string): string => {\n switch (step) {\n case AppDeploymentSteps.NOT_STARTED:\n case AppDeploymentSteps.STARTING:\n case AppDeploymentSteps.BUILDING_ASSETS:\n case AppDeploymentSteps.UPLOADING_ASSETS:\n return \"Building frontend assets.\";\n case AppDeploymentSteps.CONVERGING_STORAGE:\n return \"Setting up database.\";\n case AppDeploymentSteps.PUBLISHING_TREE:\n return `Copying ${syncJson.env.name}.`;\n case AppDeploymentSteps.RELOADING_SANDBOX:\n return \"Restarting app.\";\n case AppDeploymentSteps.COMPLETED:\n return \"Deploy complete!\";\n default:\n return \"Unknown step.\";\n }\n};\n\nexport const stepToSpinnerEnd = (syncJson: SyncJson, step: string): string => {\n switch (step) {\n case AppDeploymentSteps.NOT_STARTED:\n case AppDeploymentSteps.STARTING:\n case AppDeploymentSteps.BUILDING_ASSETS:\n case AppDeploymentSteps.UPLOADING_ASSETS:\n return `Built frontend assets. ${ts()}`;\n case AppDeploymentSteps.CONVERGING_STORAGE:\n return `Setup database. ${ts()}`;\n case AppDeploymentSteps.PUBLISHING_TREE:\n return `Copied ${syncJson.env.name}. ${ts()}`;\n case AppDeploymentSteps.RELOADING_SANDBOX:\n return `Restarted app. ${ts()}`;\n case AppDeploymentSteps.COMPLETED:\n return \"Deploy successful!\";\n default:\n return `Completed unknown step. ${ts()}`;\n }\n};\n"],"names":["assert","terminalLink","PUBLISH_STATUS_SUBSCRIPTION","DeployDisallowedError","FileSync","SyncJson","loadSyncJsonDirectory","confirm","output","println","ProblemSeverity","printProblems","publishIssuesToProblems","reportErrorAndExit","spin","sprint","ts","unreachable","isGraphQLErrors","args","PushArgs","type","String","alias","Boolean","usage","ctx","command","directory","process","cwd","syncJson","loadOrInit","ensureEmptyLineAbove","env","name","app","primaryDomain","filesync","hashes","inSync","localChangesToPush","size","onlyDotGadgetFilesChanged","print","implicitForce","isInteractive","message","bothChanged","environmentChanges","push","force","variables","localFilesVersion","filesVersion","allowCharges","spinner","currentStep","AppDeploymentSteps","NOT_STARTED","printedProblems","subscription","edit","subscribe","onError","error","log","fail","stepToSpinnerStart","cause","graphqlError","extensions","replace","exit","resubscribe","onData","publishStatus","warn","publishStarted","progress","step","issues","status","length","fatalIssues","filter","issue","severity","Fatal","problems","code","unsubscribe","COMPLETED","succeed","stepToSpinnerEnd","spinnerText","text","Object","freeze","STARTING","BUILDING_ASSETS","UPLOADING_ASSETS","CONVERGING_STORAGE","PUBLISHING_TREE","RELOADING_SANDBOX"],"mappings":"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"}