@gadgetinc/ggt 1.0.2 → 1.0.4

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 (99) hide show
  1. package/README.md +192 -128
  2. package/lib/__generated__/graphql.js.map +1 -1
  3. package/lib/commands/add.js +385 -0
  4. package/lib/commands/add.js.map +1 -0
  5. package/lib/commands/deploy.js +20 -102
  6. package/lib/commands/deploy.js.map +1 -1
  7. package/lib/commands/dev.js +43 -114
  8. package/lib/commands/dev.js.map +1 -1
  9. package/lib/commands/list.js +3 -6
  10. package/lib/commands/list.js.map +1 -1
  11. package/lib/commands/login.js +5 -5
  12. package/lib/commands/login.js.map +1 -1
  13. package/lib/commands/logout.js +2 -5
  14. package/lib/commands/logout.js.map +1 -1
  15. package/lib/commands/open.js +45 -86
  16. package/lib/commands/open.js.map +1 -1
  17. package/lib/commands/pull.js +19 -76
  18. package/lib/commands/pull.js.map +1 -1
  19. package/lib/commands/push.js +19 -76
  20. package/lib/commands/push.js.map +1 -1
  21. package/lib/commands/root.js +4 -3
  22. package/lib/commands/root.js.map +1 -1
  23. package/lib/commands/status.js +3 -8
  24. package/lib/commands/status.js.map +1 -1
  25. package/lib/commands/version.js +6 -5
  26. package/lib/commands/version.js.map +1 -1
  27. package/lib/commands/whoami.js +2 -5
  28. package/lib/commands/whoami.js.map +1 -1
  29. package/lib/ggt.js.map +1 -1
  30. package/lib/main.js.map +1 -1
  31. package/lib/services/app/api/api.js.map +1 -1
  32. package/lib/services/app/api/operation.js +11 -0
  33. package/lib/services/app/api/operation.js.map +1 -1
  34. package/lib/services/app/app.js +21 -2
  35. package/lib/services/app/app.js.map +1 -1
  36. package/lib/services/app/arg.js.map +1 -1
  37. package/lib/services/app/client.js.map +1 -1
  38. package/lib/services/app/edit/edit.js.map +1 -1
  39. package/lib/services/app/edit/operation.js +52 -0
  40. package/lib/services/app/edit/operation.js.map +1 -1
  41. package/lib/services/app/error.js.map +1 -1
  42. package/lib/services/command/arg.js +3 -1
  43. package/lib/services/command/arg.js.map +1 -1
  44. package/lib/services/command/command.js +1 -0
  45. package/lib/services/command/command.js.map +1 -1
  46. package/lib/services/command/context.js.map +1 -1
  47. package/lib/services/config/config.js.map +1 -1
  48. package/lib/services/config/env.js.map +1 -1
  49. package/lib/services/config/package-json.js.map +1 -1
  50. package/lib/services/filesync/changes.js.map +1 -1
  51. package/lib/services/filesync/conflicts.js.map +1 -1
  52. package/lib/services/filesync/directory.js.map +1 -1
  53. package/lib/services/filesync/error.js +1 -0
  54. package/lib/services/filesync/error.js.map +1 -1
  55. package/lib/services/filesync/file.js.map +1 -1
  56. package/lib/services/filesync/filesync.js +179 -174
  57. package/lib/services/filesync/filesync.js.map +1 -1
  58. package/lib/services/filesync/hashes.js.map +1 -1
  59. package/lib/services/filesync/strategy.js.map +1 -1
  60. package/lib/services/filesync/sync-json.js.map +1 -1
  61. package/lib/services/http/auth.js.map +1 -1
  62. package/lib/services/http/http.js.map +1 -1
  63. package/lib/services/output/confirm.js.map +1 -1
  64. package/lib/services/output/footer.js.map +1 -1
  65. package/lib/services/output/log/field.js.map +1 -1
  66. package/lib/services/output/log/format/format.js.map +1 -1
  67. package/lib/services/output/log/format/json.js.map +1 -1
  68. package/lib/services/output/log/format/pretty.js.map +1 -1
  69. package/lib/services/output/log/level.js.map +1 -1
  70. package/lib/services/output/log/logger.js.map +1 -1
  71. package/lib/services/output/log/structured.js.map +1 -1
  72. package/lib/services/output/notify.js.map +1 -1
  73. package/lib/services/output/output.js.map +1 -1
  74. package/lib/services/output/print.js.map +1 -1
  75. package/lib/services/output/problems.js.map +1 -1
  76. package/lib/services/output/prompt.js.map +1 -1
  77. package/lib/services/output/report.js.map +1 -1
  78. package/lib/services/output/select.js.map +1 -1
  79. package/lib/services/output/spinner.js.map +1 -1
  80. package/lib/services/output/sprint.js.map +1 -1
  81. package/lib/services/output/symbols.js.map +1 -1
  82. package/lib/services/output/table.js.map +1 -1
  83. package/lib/services/output/timestamp.js.map +1 -1
  84. package/lib/services/output/update.js.map +1 -1
  85. package/lib/services/user/session.js.map +1 -1
  86. package/lib/services/user/user.js.map +1 -1
  87. package/lib/services/util/assert.js.map +1 -1
  88. package/lib/services/util/boolean.js.map +1 -1
  89. package/lib/services/util/collection.js.map +1 -1
  90. package/lib/services/util/function.js.map +1 -1
  91. package/lib/services/util/is.js.map +1 -1
  92. package/lib/services/util/json.js.map +1 -1
  93. package/lib/services/util/number.js.map +1 -1
  94. package/lib/services/util/object.js.map +1 -1
  95. package/lib/services/util/paths.js.map +1 -1
  96. package/lib/services/util/promise.js.map +1 -1
  97. package/lib/services/util/types.js.map +1 -1
  98. package/npm-shrinkwrap.json +2183 -1763
  99. package/package.json +34 -34
@@ -40,12 +40,12 @@ import { MergeConflictPreference } from "./strategy.js";
40
40
  * single request.
41
41
  */ export const MAX_PUSH_CONTENT_LENGTH = 50 * 1024 * 1024; // 50mb
42
42
  export class FileSync {
43
- async hashes(ctx) {
44
- const spinner = spin({
43
+ async hashes(ctx, quietly) {
44
+ const spinner = !quietly ? spin({
45
45
  ensureEmptyLineAbove: true
46
46
  })`
47
47
  Calculating file changes.
48
- `;
48
+ ` : undefined;
49
49
  try {
50
50
  const [localHashes, { localFilesVersionHashes, environmentHashes, environmentFilesVersion }] = await Promise.all([
51
51
  // get the hashes of our local files
@@ -125,10 +125,12 @@ export class FileSync {
125
125
  });
126
126
  const onlyDotGadgetFilesChanged = Array.from(environmentChangesToPull.keys()).every((filepath)=>filepath.startsWith(".gadget/"));
127
127
  const bothChanged = localChanges.size > 0 && environmentChanges.size > 0 && !onlyDotGadgetFilesChanged;
128
- if (inSync) {
129
- spinner.succeed`Your files are up to date. ${ts()}`;
130
- } else {
131
- spinner.succeed`Calculated file changes. ${ts()}`;
128
+ if (spinner) {
129
+ if (inSync) {
130
+ spinner.succeed`Your files are up to date. ${ts()}`;
131
+ } else {
132
+ spinner.succeed`Calculated file changes. ${ts()}`;
133
+ }
132
134
  }
133
135
  return {
134
136
  inSync,
@@ -144,7 +146,9 @@ export class FileSync {
144
146
  bothChanged
145
147
  };
146
148
  } catch (error) {
147
- spinner.fail();
149
+ if (spinner) {
150
+ spinner.fail();
151
+ }
148
152
  throw error;
149
153
  }
150
154
  }
@@ -270,14 +274,14 @@ export class FileSync {
270
274
  changed: changed.map((file)=>file.path),
271
275
  deleted: deleted.map((file)=>file.path)
272
276
  });
273
- const changes = await this._writeToLocalFilesystem(ctx, {
277
+ const changes = await this.writeToLocalFilesystem(ctx, {
274
278
  filesVersion: remoteFilesVersion,
275
279
  files: changed,
276
280
  delete: deleted.map((file)=>file.path),
277
281
  printEnvironmentChangesOptions: {
278
282
  tense: "past",
279
283
  ensureEmptyLineAbove: true,
280
- title: sprintln`{green ${symbol.tick}} Pulled ${pluralize("file", changed.length + deleted.length)}. ${symbol.arrowLeft} ${ts()}`,
284
+ title: sprintln`{greenBright ${symbol.tick}} Pulled ${pluralize("file", changed.length + deleted.length)}. ${ts()}`,
281
285
  limit: 5,
282
286
  ...printEnvironmentChangesOptions
283
287
  }
@@ -294,13 +298,13 @@ export class FileSync {
294
298
  * - All non-conflicting changes are automatically merged.
295
299
  * - Conflicts are resolved by prompting the user to either keep their local changes or keep Gadget's changes.
296
300
  * - This function will not return until the filesystem is in sync.
297
- */ async merge(ctx, { hashes, maxAttempts = 10, printLocalChangesOptions, printEnvironmentChangesOptions } = {}) {
301
+ */ async merge(ctx, { hashes, maxAttempts = 10, printLocalChangesOptions, printEnvironmentChangesOptions, quietly } = {}) {
298
302
  let attempt = 0;
299
303
  do {
300
304
  if (attempt === 0) {
301
- hashes ??= await this.hashes(ctx);
305
+ hashes ??= await this.hashes(ctx, quietly);
302
306
  } else {
303
- hashes = await this.hashes(ctx);
307
+ hashes = await this.hashes(ctx, quietly);
304
308
  }
305
309
  if (hashes.inSync) {
306
310
  this._syncOperations.clear();
@@ -384,6 +388,166 @@ export class FileSync {
384
388
  printEnvironmentChangesOptions
385
389
  });
386
390
  }
391
+ async writeToLocalFilesystem(ctx, options) {
392
+ const filesVersion = BigInt(options.filesVersion);
393
+ assert(filesVersion >= this.syncJson.filesVersion, "filesVersion must be greater than or equal to current filesVersion");
394
+ ctx.log.debug("writing to local filesystem", {
395
+ filesVersion,
396
+ files: options.files.map((file)=>file.path),
397
+ delete: options.delete
398
+ });
399
+ const changes = new Changes();
400
+ const directoriesWithDeletedFiles = new Set();
401
+ await pMap(options.delete, async (pathToDelete)=>{
402
+ // add all the directories that contain this file to
403
+ // directoriesWithDeletedFiles so we can clean them up later
404
+ let dir = path.dirname(pathToDelete);
405
+ while(dir !== "."){
406
+ directoriesWithDeletedFiles.add(this.syncJson.directory.normalize(dir, true));
407
+ dir = path.dirname(dir);
408
+ }
409
+ const currentPath = this.syncJson.directory.absolute(pathToDelete);
410
+ const backupPath = this.syncJson.directory.absolute(".gadget/backup", this.syncJson.directory.relative(pathToDelete));
411
+ // rather than `rm -rf`ing files, we move them to
412
+ // `.gadget/backup/` so that users can recover them if something
413
+ // goes wrong. We've seen a lot of EBUSY/EINVAL errors when moving
414
+ // files so we retry a few times.
415
+ await pRetry(async ()=>{
416
+ try {
417
+ // remove the current backup file in case it exists and is a
418
+ // different type (file vs directory)
419
+ await fs.remove(backupPath);
420
+ await fs.move(currentPath, backupPath);
421
+ changes.set(pathToDelete, {
422
+ type: "delete"
423
+ });
424
+ } catch (error) {
425
+ if (isENOENTError(error)) {
426
+ // replicate the behavior of `rm -rf` and ignore ENOENT
427
+ return;
428
+ }
429
+ if (isENOTDIRError(error) || isEEXISTError(error)) {
430
+ // the backup path already exists and ends in a file
431
+ // rather than a directory, so we have to remove the file
432
+ // before we can move the current path to the backup path
433
+ let dir = path.dirname(backupPath);
434
+ while(dir !== this.syncJson.directory.absolute(".gadget/backup")){
435
+ const stats = await fs.stat(dir);
436
+ // eslint-disable-next-line max-depth
437
+ if (!stats.isDirectory()) {
438
+ // this file is in the way, so remove it
439
+ ctx.log.debug("removing file in the way of backup path", {
440
+ currentPath,
441
+ backupPath,
442
+ file: dir
443
+ });
444
+ await fs.remove(dir);
445
+ }
446
+ dir = path.dirname(dir);
447
+ }
448
+ // still throw the error so we retry
449
+ }
450
+ throw error;
451
+ }
452
+ }, {
453
+ // windows tends to run into these issues way more often than
454
+ // mac/linux, so we retry more times
455
+ retries: config.windows ? 4 : 2,
456
+ minTimeout: ms("100ms"),
457
+ onFailedAttempt: (error)=>{
458
+ ctx.log.warn("failed to move file to backup", {
459
+ error,
460
+ currentPath,
461
+ backupPath
462
+ });
463
+ }
464
+ });
465
+ });
466
+ for (const directoryWithDeletedFile of Array.from(directoriesWithDeletedFiles.values()).sort().reverse()){
467
+ if (options.files.some((file)=>file.path === directoryWithDeletedFile)) {
468
+ continue;
469
+ }
470
+ try {
471
+ // delete any empty directories that contained a deleted file.
472
+ // if the empty directory should continue to exist, we would
473
+ // have received an event to create it above
474
+ await fs.rmdir(this.syncJson.directory.absolute(directoryWithDeletedFile));
475
+ changes.set(directoryWithDeletedFile, {
476
+ type: "delete"
477
+ });
478
+ } catch (error) {
479
+ if (isENOENTError(error) || isENOTEMPTYError(error)) {
480
+ continue;
481
+ }
482
+ throw error;
483
+ }
484
+ }
485
+ await pMap(options.files, async (file)=>{
486
+ const absolutePath = this.syncJson.directory.absolute(file.path);
487
+ if (await fs.pathExists(absolutePath)) {
488
+ if (!file.path.endsWith("/")) {
489
+ // only track file updates, not directory updates
490
+ changes.set(file.path, {
491
+ type: "update"
492
+ });
493
+ }
494
+ } else {
495
+ changes.set(file.path, {
496
+ type: "create"
497
+ });
498
+ }
499
+ if (file.path.endsWith("/")) {
500
+ await fs.ensureDir(absolutePath);
501
+ } else {
502
+ await fs.outputFile(absolutePath, Buffer.from(file.content, file.encoding));
503
+ }
504
+ if (supportsPermissions) {
505
+ // the os's default umask makes setting the mode during creation
506
+ // not work, so an additional fs.chmod call is necessary to
507
+ // ensure the file has the correct mode
508
+ await fs.chmod(absolutePath, file.mode & 0o777);
509
+ }
510
+ if (absolutePath === this.syncJson.directory.absolute(".ignore")) {
511
+ await this.syncJson.directory.loadIgnoreFile();
512
+ }
513
+ });
514
+ await this.syncJson.save(String(filesVersion));
515
+ options.spinner?.clear();
516
+ printChanges(ctx, {
517
+ changes,
518
+ tense: "past",
519
+ title: sprint`{greenBright ${symbol.arrowDown}} Pulled ${pluralize("file", changes.size)}. ${ts()}`,
520
+ ...options.printEnvironmentChangesOptions,
521
+ includeDotGadget: !!ctx.args["--verbose"]
522
+ });
523
+ if (changes.has("yarn.lock")) {
524
+ const spinner = spin({
525
+ ensureEmptyLineAbove: true
526
+ })('Running "yarn install --check-files"');
527
+ try {
528
+ await execa("yarn", [
529
+ "install",
530
+ "--check-files"
531
+ ], {
532
+ cwd: this.syncJson.directory.path
533
+ });
534
+ spinner.succeed`Ran "yarn install --check-files" ${ts()}`;
535
+ } catch (error) {
536
+ spinner.fail();
537
+ ctx.log.error("yarn install failed", {
538
+ error
539
+ });
540
+ const message = serializeError(error).message;
541
+ if (message) {
542
+ println({
543
+ ensureEmptyLineAbove: true,
544
+ indent: 2
545
+ })(message);
546
+ }
547
+ }
548
+ }
549
+ return changes;
550
+ }
387
551
  async _merge(ctx, { hashes: { localChanges, environmentChanges, environmentFilesVersion }, printLocalChangesOptions, printEnvironmentChangesOptions }) {
388
552
  const conflicts = getConflicts({
389
553
  localChanges,
@@ -455,7 +619,7 @@ export class FileSync {
455
619
  })(sprintChanges(ctx, {
456
620
  changes,
457
621
  tense: "present",
458
- title: sprint`Pulling ${pluralize("file", changes.size)}. ${symbol.arrowLeft}`,
622
+ title: sprint`Pulling ${pluralize("file", changes.size)}.`,
459
623
  ...printEnvironmentChangesOptions
460
624
  }));
461
625
  try {
@@ -474,7 +638,7 @@ export class FileSync {
474
638
  });
475
639
  files = fileSyncFiles.files;
476
640
  }
477
- await this._writeToLocalFilesystem(ctx, {
641
+ await this.writeToLocalFilesystem(ctx, {
478
642
  filesVersion,
479
643
  files,
480
644
  delete: changes.deleted(),
@@ -611,165 +775,6 @@ export class FileSync {
611
775
  throw error;
612
776
  }
613
777
  }
614
- async _writeToLocalFilesystem(ctx, options) {
615
- const filesVersion = BigInt(options.filesVersion);
616
- assert(filesVersion >= this.syncJson.filesVersion, "filesVersion must be greater than or equal to current filesVersion");
617
- ctx.log.debug("writing to local filesystem", {
618
- filesVersion,
619
- files: options.files.map((file)=>file.path),
620
- delete: options.delete
621
- });
622
- const changes = new Changes();
623
- const directoriesWithDeletedFiles = new Set();
624
- await pMap(options.delete, async (pathToDelete)=>{
625
- // add all the directories that contain this file to
626
- // directoriesWithDeletedFiles so we can clean them up later
627
- let dir = path.dirname(pathToDelete);
628
- while(dir !== "."){
629
- directoriesWithDeletedFiles.add(this.syncJson.directory.normalize(dir, true));
630
- dir = path.dirname(dir);
631
- }
632
- const currentPath = this.syncJson.directory.absolute(pathToDelete);
633
- const backupPath = this.syncJson.directory.absolute(".gadget/backup", this.syncJson.directory.relative(pathToDelete));
634
- // rather than `rm -rf`ing files, we move them to
635
- // `.gadget/backup/` so that users can recover them if something
636
- // goes wrong. We've seen a lot of EBUSY/EINVAL errors when moving
637
- // files so we retry a few times.
638
- await pRetry(async ()=>{
639
- try {
640
- // remove the current backup file in case it exists and is a
641
- // different type (file vs directory)
642
- await fs.remove(backupPath);
643
- await fs.move(currentPath, backupPath);
644
- changes.set(pathToDelete, {
645
- type: "delete"
646
- });
647
- } catch (error) {
648
- if (isENOENTError(error)) {
649
- // replicate the behavior of `rm -rf` and ignore ENOENT
650
- return;
651
- }
652
- if (isENOTDIRError(error) || isEEXISTError(error)) {
653
- // the backup path already exists and ends in a file
654
- // rather than a directory, so we have to remove the file
655
- // before we can move the current path to the backup path
656
- let dir = path.dirname(backupPath);
657
- while(dir !== this.syncJson.directory.absolute(".gadget/backup")){
658
- const stats = await fs.stat(dir);
659
- // eslint-disable-next-line max-depth
660
- if (!stats.isDirectory()) {
661
- // this file is in the way, so remove it
662
- ctx.log.debug("removing file in the way of backup path", {
663
- currentPath,
664
- backupPath,
665
- file: dir
666
- });
667
- await fs.remove(dir);
668
- }
669
- dir = path.dirname(dir);
670
- }
671
- // still throw the error so we retry
672
- }
673
- throw error;
674
- }
675
- }, {
676
- // windows tends to run into these issues way more often than
677
- // mac/linux, so we retry more times
678
- retries: config.windows ? 4 : 2,
679
- minTimeout: ms("100ms"),
680
- onFailedAttempt: (error)=>{
681
- ctx.log.warn("failed to move file to backup", {
682
- error,
683
- currentPath,
684
- backupPath
685
- });
686
- }
687
- });
688
- });
689
- for (const directoryWithDeletedFile of Array.from(directoriesWithDeletedFiles.values()).sort().reverse()){
690
- if (options.files.some((file)=>file.path === directoryWithDeletedFile)) {
691
- continue;
692
- }
693
- try {
694
- // delete any empty directories that contained a deleted file.
695
- // if the empty directory should continue to exist, we would
696
- // have received an event to create it above
697
- await fs.rmdir(this.syncJson.directory.absolute(directoryWithDeletedFile));
698
- changes.set(directoryWithDeletedFile, {
699
- type: "delete"
700
- });
701
- } catch (error) {
702
- if (isENOENTError(error) || isENOTEMPTYError(error)) {
703
- continue;
704
- }
705
- throw error;
706
- }
707
- }
708
- await pMap(options.files, async (file)=>{
709
- const absolutePath = this.syncJson.directory.absolute(file.path);
710
- if (await fs.pathExists(absolutePath)) {
711
- if (!file.path.endsWith("/")) {
712
- // only track file updates, not directory updates
713
- changes.set(file.path, {
714
- type: "update"
715
- });
716
- }
717
- } else {
718
- changes.set(file.path, {
719
- type: "create"
720
- });
721
- }
722
- if (file.path.endsWith("/")) {
723
- await fs.ensureDir(absolutePath);
724
- } else {
725
- await fs.outputFile(absolutePath, Buffer.from(file.content, file.encoding));
726
- }
727
- if (supportsPermissions) {
728
- // the os's default umask makes setting the mode during creation
729
- // not work, so an additional fs.chmod call is necessary to
730
- // ensure the file has the correct mode
731
- await fs.chmod(absolutePath, file.mode & 0o777);
732
- }
733
- if (absolutePath === this.syncJson.directory.absolute(".ignore")) {
734
- await this.syncJson.directory.loadIgnoreFile();
735
- }
736
- });
737
- await this.syncJson.save(String(filesVersion));
738
- options.spinner?.clear();
739
- printChanges(ctx, {
740
- changes,
741
- tense: "past",
742
- title: sprint`{green ${symbol.tick}} Pulled ${pluralize("file", changes.size)}. ${symbol.arrowLeft} ${ts()}`,
743
- ...options.printEnvironmentChangesOptions
744
- });
745
- if (changes.has("yarn.lock")) {
746
- const spinner = spin({
747
- ensureEmptyLineAbove: true
748
- })('Running "yarn install --check-files"');
749
- try {
750
- await execa("yarn", [
751
- "install",
752
- "--check-files"
753
- ], {
754
- cwd: this.syncJson.directory.path
755
- });
756
- spinner.succeed`Ran "yarn install --check-files" ${ts()}`;
757
- } catch (error) {
758
- spinner.fail();
759
- ctx.log.error("yarn install failed", {
760
- error
761
- });
762
- const message = serializeError(error).message;
763
- if (message) {
764
- println({
765
- ensureEmptyLineAbove: true,
766
- indent: 2
767
- })(message);
768
- }
769
- }
770
- }
771
- return changes;
772
- }
773
778
  constructor(syncJson){
774
779
  _define_property(this, "syncJson", void 0);
775
780
  /**