ardent-cli 0.0.38 → 0.0.40

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 (2) hide show
  1. package/dist/index.js +390 -64
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -460,9 +460,13 @@ function getAnonymousId() {
460
460
  }
461
461
  function trackEvent(event, properties = {}) {
462
462
  const distinctId = getConfig("userId") ?? getAnonymousId();
463
+ const controller = new AbortController();
464
+ const timeout = setTimeout(() => controller.abort(), 500);
465
+ timeout.unref?.();
463
466
  fetch(`${getApiUrl()}/v1/posthog/event`, {
464
467
  method: "POST",
465
468
  headers: { "Content-Type": "application/json" },
469
+ signal: controller.signal,
466
470
  body: JSON.stringify({
467
471
  events: [{
468
472
  event,
@@ -474,14 +478,18 @@ function trackEvent(event, properties = {}) {
474
478
  }]
475
479
  })
476
480
  }).catch(() => {
477
- });
481
+ }).finally(() => clearTimeout(timeout));
478
482
  }
479
483
  function identifyUser(userId, personProperties = {}) {
480
484
  const anonymousId = getAnonymousId();
481
485
  setConfig("userId", userId);
486
+ const controller = new AbortController();
487
+ const timeout = setTimeout(() => controller.abort(), 500);
488
+ timeout.unref?.();
482
489
  fetch(`${getApiUrl()}/v1/posthog/event`, {
483
490
  method: "POST",
484
491
  headers: { "Content-Type": "application/json" },
492
+ signal: controller.signal,
485
493
  body: JSON.stringify({
486
494
  events: [{
487
495
  event: "$identify",
@@ -494,7 +502,7 @@ function identifyUser(userId, personProperties = {}) {
494
502
  }]
495
503
  })
496
504
  }).catch(() => {
497
- });
505
+ }).finally(() => clearTimeout(timeout));
498
506
  }
499
507
 
500
508
  // src/commands/branch/create.ts
@@ -796,6 +804,126 @@ function printDegradedWarnings(warnings) {
796
804
  console.log(" Review this connector with: ardent connector list");
797
805
  }
798
806
 
807
+ // src/lib/operation_stage_display.ts
808
+ var STAGE_DISPLAY = {
809
+ // engine_setup_worker / postgres_engine_setup
810
+ "dispatched": "Starting",
811
+ "preparing": "Preparing",
812
+ "creating-neon-project": "Provisioning the branch target",
813
+ "preparing-target-databases": "Preparing target databases",
814
+ "deploying-pgstream": "Starting replication",
815
+ "applying-rls": "Applying RLS policies",
816
+ "storing-credentials": "Storing connection credentials",
817
+ "validating": "Validating the branch target",
818
+ // reset_worker / reset_connector
819
+ "resetting": "Resetting",
820
+ "deleting-pgstream": "Stopping replication",
821
+ "rediscovering-source": "Re-checking the source database",
822
+ "resetting-neon-main": "Resetting the branch target",
823
+ "creating-target-schemas": "Recreating target schemas",
824
+ "redeploying-pgstream": "Restarting replication",
825
+ // environment deploy_worker
826
+ "loading_config": "Loading environment configuration",
827
+ "deploying_infrastructure": "Provisioning environment infrastructure",
828
+ "recording_success": "Finalizing environment",
829
+ "cleaning_failed_deploy": "Cleaning up failed environment provisioning",
830
+ // environment destroy_worker
831
+ "deleting_private_links": "Removing private network links",
832
+ "destroying_infrastructure": "Tearing down environment infrastructure",
833
+ "recording_destroy_success": "Finalizing environment teardown",
834
+ // discovery_worker / run_discovery_crawl (ARD-1098)
835
+ "connecting": "Connecting to source",
836
+ "enumerating_databases": "Listing databases",
837
+ "scanning_database": "Scanning schema",
838
+ "writing_schema": "Saving discovered schema",
839
+ "merging_results": "Combining results"
840
+ };
841
+ function humanizeRawStage(_raw) {
842
+ return "Working";
843
+ }
844
+ function operationStageDisplay(stage) {
845
+ if (!stage) return "Working";
846
+ const colonIndex = stage.indexOf(":");
847
+ const base = colonIndex === -1 ? stage : stage.slice(0, colonIndex);
848
+ const suffix = colonIndex === -1 ? "" : stage.slice(colonIndex + 1);
849
+ const label = STAGE_DISPLAY[base] ?? humanizeRawStage(base);
850
+ return suffix ? `${label} (for ${suffix})` : label;
851
+ }
852
+
853
+ // src/lib/discover.ts
854
+ var DiscoveryTimeoutError = class extends Error {
855
+ constructor(message) {
856
+ super(message);
857
+ this.name = "DiscoveryTimeoutError";
858
+ }
859
+ };
860
+ var DiscoveryOperationFailedError = class extends Error {
861
+ operationError;
862
+ constructor(operationError) {
863
+ super(
864
+ `Discovery failed: ${operationError ?? "unknown error"}. Inspect the connector with \`ardent connector list\`, fix the underlying issue, then re-run \`ardent connector create\` or \`ardent connector rediscover\`.`
865
+ );
866
+ this.name = "DiscoveryOperationFailedError";
867
+ this.operationError = operationError;
868
+ }
869
+ };
870
+ async function runDiscoveryWithPolling(connectorId, options) {
871
+ const maxWaitMs = options?.maxWaitMs ?? 10 * 60 * 1e3;
872
+ const pollIntervalMs = options?.pollIntervalMs ?? 3 * 1e3;
873
+ let dispatch;
874
+ try {
875
+ dispatch = await api.post(
876
+ `/v1/connectors/${connectorId}/discover`,
877
+ {}
878
+ );
879
+ } catch (err) {
880
+ if (isGatewayTimeoutError(err)) {
881
+ throw new Error(
882
+ "Backend dispatch timed out. The /discover endpoint is now async and should respond in under a few seconds; this may indicate a backend incident. Try again, or check `ardent connector list` to see if a discovery operation was created anyway."
883
+ );
884
+ }
885
+ throw err;
886
+ }
887
+ if (options?.onPrerequisites) {
888
+ options.onPrerequisites(dispatch.prerequisites, dispatch.source_metadata);
889
+ }
890
+ const operationId = dispatch.operation_id;
891
+ const startedAt = Date.now();
892
+ let lastStage = null;
893
+ while (Date.now() - startedAt < maxWaitMs) {
894
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
895
+ let op;
896
+ try {
897
+ op = await api.get(`/v1/operations/${operationId}`);
898
+ } catch (pollErr) {
899
+ if (isGatewayTimeoutError(pollErr)) continue;
900
+ throw pollErr;
901
+ }
902
+ if (op.stage && op.stage !== lastStage) {
903
+ const progressLabel = op.progress != null ? ` (${op.progress}%)` : "";
904
+ console.log(` ${operationStageDisplay(op.stage)}${progressLabel}`);
905
+ lastStage = op.stage;
906
+ }
907
+ if (op.status === "completed") {
908
+ const result = op.result ?? {};
909
+ const schemaVersion = typeof result.schema_version === "number" ? result.schema_version : 0;
910
+ const databasesCount = typeof result.databases_count === "number" ? result.databases_count : 0;
911
+ return {
912
+ prerequisites: dispatch.prerequisites,
913
+ source_metadata: dispatch.source_metadata,
914
+ schema_version: schemaVersion,
915
+ databases_count: databasesCount
916
+ };
917
+ }
918
+ if (op.status === "failed") {
919
+ throw new DiscoveryOperationFailedError(op.error);
920
+ }
921
+ }
922
+ throw new DiscoveryTimeoutError(
923
+ `Discovery did not complete within ${Math.round(maxWaitMs / 6e4)} minutes. The catalog walk may still be running server-side \u2014 do NOT delete the connector. Check status with: ardent connector list. If you contact Ardent support, reference operation id ${operationId}.`
924
+ );
925
+ }
926
+
799
927
  // src/lib/connector_status_display.ts
800
928
  var ENGINE_STATUS_DISPLAY = {
801
929
  healthy: "ready",
@@ -882,46 +1010,6 @@ function assertEngineSetupCompleted(operation, connectorName) {
882
1010
  );
883
1011
  }
884
1012
 
885
- // src/lib/operation_stage_display.ts
886
- var STAGE_DISPLAY = {
887
- // engine_setup_worker / postgres_engine_setup
888
- "dispatched": "Starting",
889
- "preparing": "Preparing",
890
- "creating-neon-project": "Provisioning the branch target",
891
- "preparing-target-databases": "Preparing target databases",
892
- "deploying-pgstream": "Starting replication",
893
- "applying-rls": "Applying RLS policies",
894
- "storing-credentials": "Storing connection credentials",
895
- "validating": "Validating the branch target",
896
- // reset_worker / reset_connector
897
- "resetting": "Resetting",
898
- "deleting-pgstream": "Stopping replication",
899
- "rediscovering-source": "Re-checking the source database",
900
- "resetting-neon-main": "Resetting the branch target",
901
- "creating-target-schemas": "Recreating target schemas",
902
- "redeploying-pgstream": "Restarting replication",
903
- // environment deploy_worker
904
- "loading_config": "Loading environment configuration",
905
- "deploying_infrastructure": "Provisioning environment infrastructure",
906
- "recording_success": "Finalizing environment",
907
- "cleaning_failed_deploy": "Cleaning up failed environment provisioning",
908
- // environment destroy_worker
909
- "deleting_private_links": "Removing private network links",
910
- "destroying_infrastructure": "Tearing down environment infrastructure",
911
- "recording_destroy_success": "Finalizing environment teardown"
912
- };
913
- function humanizeRawStage(_raw) {
914
- return "Working";
915
- }
916
- function operationStageDisplay(stage) {
917
- if (!stage) return "Working";
918
- const colonIndex = stage.indexOf(":");
919
- const base = colonIndex === -1 ? stage : stage.slice(0, colonIndex);
920
- const suffix = colonIndex === -1 ? "" : stage.slice(colonIndex + 1);
921
- const label = STAGE_DISPLAY[base] ?? humanizeRawStage(base);
922
- return suffix ? `${label} (for ${suffix})` : label;
923
- }
924
-
925
1013
  // src/lib/engine_setup.ts
926
1014
  var EngineSetupTimeoutError = class extends Error {
927
1015
  constructor(message) {
@@ -1697,11 +1785,23 @@ async function createAction2(type, url, options) {
1697
1785
  }
1698
1786
  } else {
1699
1787
  console.log("Discovering schema...");
1700
- const discoverResult = await api.post(
1701
- `/v1/connectors/${connectorId}/discover`,
1702
- {}
1703
- );
1704
- const discoveryWarnings = discoverResult?.source_metadata?.warnings ?? [];
1788
+ let discoverPollResult;
1789
+ try {
1790
+ discoverPollResult = await runDiscoveryWithPolling(connectorId, {
1791
+ onPrerequisites: (prereqs) => {
1792
+ if (prereqs.pass) {
1793
+ console.log("Connected \u2713 \u2014 scanning your schema\u2026");
1794
+ }
1795
+ }
1796
+ });
1797
+ } catch (discoverErr) {
1798
+ if (discoverErr instanceof DiscoveryTimeoutError || discoverErr instanceof DiscoveryOperationFailedError) {
1799
+ console.error(`\u2717 ${discoverErr.message}`);
1800
+ process.exit(1);
1801
+ }
1802
+ throw discoverErr;
1803
+ }
1804
+ const discoveryWarnings = discoverPollResult.source_metadata?.warnings ?? [];
1705
1805
  if (discoveryWarnings.length > 0) {
1706
1806
  const yellow = "\x1B[33m";
1707
1807
  const reset2 = "\x1B[0m";
@@ -2944,6 +3044,7 @@ var ORIGINAL_SUFFIX = "-ardent-original";
2944
3044
  var SERVICE_SUFFIXES = ["rest", "auth", "storage", "realtime", "pg_meta"];
2945
3045
  var DOCKER_HOST_GATEWAY = "host.docker.internal";
2946
3046
  var REALTIME_LIST_CHANGES_MIGRATION_VERSION = "20230328144023";
3047
+ var MIGRATION_IMPORT_TIMEOUT_MS = 15e3;
2947
3048
  var SENSITIVE_DOCKER_ENV_KEYS = /* @__PURE__ */ new Set([
2948
3049
  "DATABASE_URL",
2949
3050
  "DB_PASSWORD",
@@ -3521,19 +3622,68 @@ function dumpLocalStorageMigrations(dbContainer) {
3521
3622
  "--no-privileges"
3522
3623
  ]);
3523
3624
  }
3625
+ function dumpLocalRealtimeMigrations(dbContainer) {
3626
+ return runDocker([
3627
+ "exec",
3628
+ dbContainer,
3629
+ "pg_dump",
3630
+ "-U",
3631
+ "postgres",
3632
+ "-d",
3633
+ "postgres",
3634
+ "--data-only",
3635
+ "--table=realtime.schema_migrations",
3636
+ "--column-inserts",
3637
+ "--no-owner",
3638
+ "--no-privileges"
3639
+ ]);
3640
+ }
3524
3641
  function storageMigrationsImportSql(dumpSql) {
3525
3642
  const tempTable = "pg_temp.ardent_storage_migrations_import";
3526
- const tempDumpSql = dumpSql.replaceAll("INSERT INTO storage.migrations", `INSERT INTO ${tempTable}`);
3643
+ const tempDumpSql = retargetPgDumpInserts(dumpSql, "storage", "migrations", tempTable);
3527
3644
  return `
3645
+ SET lock_timeout = '2s';
3646
+ SET statement_timeout = '10s';
3528
3647
  BEGIN;
3529
3648
  CREATE TEMP TABLE ardent_storage_migrations_import (LIKE storage.migrations INCLUDING DEFAULTS);
3530
3649
  ${tempDumpSql}
3531
3650
  INSERT INTO storage.migrations
3532
3651
  SELECT * FROM ${tempTable}
3533
3652
  ON CONFLICT DO NOTHING;
3653
+ COMMIT;
3654
+ `;
3655
+ }
3656
+ function realtimeMigrationsImportSql(dumpSql) {
3657
+ const tempTable = "pg_temp.ardent_realtime_migrations_import";
3658
+ const tempDumpSql = retargetPgDumpInserts(dumpSql, "realtime", "schema_migrations", tempTable);
3659
+ return `
3660
+ SET lock_timeout = '2s';
3661
+ SET statement_timeout = '10s';
3662
+ BEGIN;
3663
+ CREATE SCHEMA IF NOT EXISTS realtime;
3664
+ CREATE TABLE IF NOT EXISTS realtime.schema_migrations (
3665
+ version bigint PRIMARY KEY,
3666
+ inserted_at timestamp(0) without time zone
3667
+ );
3668
+ CREATE TEMP TABLE ardent_realtime_migrations_import (LIKE realtime.schema_migrations INCLUDING DEFAULTS);
3669
+ ${tempDumpSql}
3670
+ INSERT INTO realtime.schema_migrations
3671
+ SELECT * FROM ${tempTable}
3672
+ ON CONFLICT DO NOTHING;
3534
3673
  COMMIT;
3535
3674
  `;
3536
3675
  }
3676
+ function retargetPgDumpInserts(dumpSql, schema, table, tempTable) {
3677
+ const insertPattern = new RegExp(
3678
+ `INSERT INTO (?:${schema}\\.${table}|"${schema}"\\."${table}")`,
3679
+ "g"
3680
+ );
3681
+ const tempDumpSql = dumpSql.replace(insertPattern, `INSERT INTO ${tempTable}`);
3682
+ if (/\bINSERT INTO\b/.test(dumpSql) && tempDumpSql === dumpSql) {
3683
+ throw new Error(`Could not rewrite pg_dump INSERT statements for ${schema}.${table}.`);
3684
+ }
3685
+ return tempDumpSql;
3686
+ }
3537
3687
  function waitForOriginalDb(containerName2) {
3538
3688
  const deadline = Date.now() + 3e4;
3539
3689
  while (Date.now() < deadline) {
@@ -3546,6 +3696,81 @@ function waitForOriginalDb(containerName2) {
3546
3696
  }
3547
3697
  throw new Error(`Timed out waiting for ${containerName2} to accept local PostgreSQL connections.`);
3548
3698
  }
3699
+ function waitForBranchProxy(originalDbContainer, dbContainer, network, branchUser, branchPassword) {
3700
+ runDocker(["start", originalDbContainer]);
3701
+ try {
3702
+ waitForOriginalDb(originalDbContainer);
3703
+ const dbContainerIpAddress = containerIpAddress(dbContainer, network);
3704
+ const deadline = Date.now() + 6e4;
3705
+ let lastError = "";
3706
+ while (Date.now() < deadline) {
3707
+ try {
3708
+ runDocker([
3709
+ "exec",
3710
+ "-e",
3711
+ `PGPASSWORD=${branchPassword}`,
3712
+ originalDbContainer,
3713
+ "psql",
3714
+ "-h",
3715
+ dbContainerIpAddress,
3716
+ "-p",
3717
+ "5432",
3718
+ "-U",
3719
+ branchUser,
3720
+ "-d",
3721
+ "postgres",
3722
+ "-v",
3723
+ "ON_ERROR_STOP=1",
3724
+ "-c",
3725
+ "SELECT 1"
3726
+ ]);
3727
+ return;
3728
+ } catch (error) {
3729
+ lastError = error instanceof Error ? error.message : String(error);
3730
+ execFileSync("sleep", ["1"]);
3731
+ }
3732
+ }
3733
+ throw new Error(`Timed out waiting for branch DB proxy to accept connections: ${lastError}`);
3734
+ } finally {
3735
+ runDocker(["stop", originalDbContainer]);
3736
+ }
3737
+ }
3738
+ function waitForBranchProxyWithClientImage(clientImage, network, dbContainer, branchUser, branchPassword) {
3739
+ const deadline = Date.now() + 6e4;
3740
+ let lastError = "";
3741
+ while (Date.now() < deadline) {
3742
+ try {
3743
+ runDocker([
3744
+ "run",
3745
+ "--rm",
3746
+ "--network",
3747
+ network,
3748
+ "--entrypoint",
3749
+ "psql",
3750
+ "-e",
3751
+ `PGPASSWORD=${branchPassword}`,
3752
+ clientImage,
3753
+ "-h",
3754
+ dbContainer,
3755
+ "-p",
3756
+ "5432",
3757
+ "-U",
3758
+ branchUser,
3759
+ "-d",
3760
+ "postgres",
3761
+ "-v",
3762
+ "ON_ERROR_STOP=1",
3763
+ "-c",
3764
+ "SELECT 1"
3765
+ ]);
3766
+ return;
3767
+ } catch (error) {
3768
+ lastError = error instanceof Error ? error.message : String(error);
3769
+ execFileSync("sleep", ["1"]);
3770
+ }
3771
+ }
3772
+ throw new Error(`Timed out waiting for final branch DB proxy to accept connections: ${lastError}`);
3773
+ }
3549
3774
  function copyLocalStorageMigrationsToBranch(dumpSql, originalDbContainer, dbContainer, network, branchUser, branchPassword) {
3550
3775
  runDocker(["start", originalDbContainer]);
3551
3776
  try {
@@ -3570,7 +3795,39 @@ function copyLocalStorageMigrationsToBranch(dumpSql, originalDbContainer, dbCont
3570
3795
  "-v",
3571
3796
  "ON_ERROR_STOP=1"
3572
3797
  ],
3573
- storageMigrationsImportSql(dumpSql)
3798
+ storageMigrationsImportSql(dumpSql),
3799
+ { timeoutMs: MIGRATION_IMPORT_TIMEOUT_MS }
3800
+ );
3801
+ } finally {
3802
+ runDocker(["stop", originalDbContainer]);
3803
+ }
3804
+ }
3805
+ function copyLocalRealtimeMigrationsToBranch(dumpSql, originalDbContainer, dbContainer, network, branchPassword) {
3806
+ runDocker(["start", originalDbContainer]);
3807
+ try {
3808
+ waitForOriginalDb(originalDbContainer);
3809
+ const dbContainerIpAddress = containerIpAddress(dbContainer, network);
3810
+ runDockerWithInput(
3811
+ [
3812
+ "exec",
3813
+ "-i",
3814
+ "-e",
3815
+ `PGPASSWORD=${branchPassword}`,
3816
+ originalDbContainer,
3817
+ "psql",
3818
+ "-h",
3819
+ dbContainerIpAddress,
3820
+ "-p",
3821
+ "5432",
3822
+ "-U",
3823
+ "supabase_admin",
3824
+ "-d",
3825
+ "postgres",
3826
+ "-v",
3827
+ "ON_ERROR_STOP=1"
3828
+ ],
3829
+ realtimeMigrationsImportSql(dumpSql),
3830
+ { timeoutMs: MIGRATION_IMPORT_TIMEOUT_MS }
3574
3831
  );
3575
3832
  } finally {
3576
3833
  runDocker(["stop", originalDbContainer]);
@@ -3691,7 +3948,6 @@ function applySupabaseRealtimeCompatibility(projectId, originalDbContainer, dbCo
3691
3948
  supabaseRealtimeCompatibilitySql(),
3692
3949
  { timeoutMs: 7e3 }
3693
3950
  );
3694
- runDocker(["restart", realtimeContainer]);
3695
3951
  return;
3696
3952
  } catch (error) {
3697
3953
  lastError = error instanceof Error ? error.message : String(error);
@@ -3713,6 +3969,42 @@ function kongApiUrl(projectId) {
3713
3969
  const port = first?.split(":").pop();
3714
3970
  return port ? `http://127.0.0.1:${port}` : void 0;
3715
3971
  }
3972
+ function requireLinkedServiceContainersRunning(projectId) {
3973
+ const stoppedServices = [];
3974
+ for (const servicePrefix of SERVICE_SUFFIXES) {
3975
+ const name = containerName(servicePrefix, projectId);
3976
+ if (!dockerExists(name)) {
3977
+ continue;
3978
+ }
3979
+ const state = runDocker([
3980
+ "inspect",
3981
+ "-f",
3982
+ "{{.State.Status}}",
3983
+ name
3984
+ ]);
3985
+ if (state !== "running") {
3986
+ stoppedServices.push(`${name}=${state}`);
3987
+ }
3988
+ }
3989
+ if (stoppedServices.length > 0) {
3990
+ throw new Error(`Linked Supabase service container is not running: ${stoppedServices.join(", ")}`);
3991
+ }
3992
+ }
3993
+ async function closeHealthResponse(response) {
3994
+ try {
3995
+ await response.body?.cancel();
3996
+ } catch {
3997
+ }
3998
+ }
3999
+ async function fetchHealthCheck(url) {
4000
+ const controller = new AbortController();
4001
+ const timeout = setTimeout(() => controller.abort(), 5e3);
4002
+ try {
4003
+ return await fetch(url, { signal: controller.signal });
4004
+ } finally {
4005
+ clearTimeout(timeout);
4006
+ }
4007
+ }
3716
4008
  async function waitForHealth(projectId) {
3717
4009
  const apiUrl = kongApiUrl(projectId);
3718
4010
  if (!apiUrl) {
@@ -3722,12 +4014,22 @@ async function waitForHealth(projectId) {
3722
4014
  let lastError = "";
3723
4015
  while (Date.now() < deadline) {
3724
4016
  try {
3725
- const authResponse = await fetch(`${apiUrl}/auth/v1/health`);
3726
- const restResponse = await fetch(`${apiUrl}/rest/v1/`);
3727
- if (authResponse.ok && restResponse.ok) {
3728
- return;
4017
+ requireLinkedServiceContainersRunning(projectId);
4018
+ let authResponse;
4019
+ let restResponse;
4020
+ try {
4021
+ authResponse = await fetchHealthCheck(`${apiUrl}/auth/v1/health`);
4022
+ restResponse = await fetchHealthCheck(`${apiUrl}/rest/v1/`);
4023
+ if (authResponse.ok && restResponse.ok) {
4024
+ return;
4025
+ }
4026
+ lastError = `auth=${authResponse.status}, rest=${restResponse.status}`;
4027
+ } finally {
4028
+ await Promise.all([
4029
+ authResponse ? closeHealthResponse(authResponse) : Promise.resolve(),
4030
+ restResponse ? closeHealthResponse(restResponse) : Promise.resolve()
4031
+ ]);
3729
4032
  }
3730
- lastError = `auth=${authResponse.status}, rest=${restResponse.status}`;
3731
4033
  } catch (error) {
3732
4034
  lastError = error instanceof Error ? error.message : String(error);
3733
4035
  }
@@ -3794,11 +4096,16 @@ async function linkSupabaseAction(branchName, options = {}) {
3794
4096
  }
3795
4097
  console.log(`Linking Supabase project ${projectId} to branch ${branch.name}...`);
3796
4098
  const storageMigrationsDump = dumpLocalStorageMigrations(dbContainer);
4099
+ const realtimeContainer = containerName("realtime", projectId);
4100
+ const realtimeMigrationsDump = dockerExists(realtimeContainer) ? dumpLocalRealtimeMigrations(dbContainer) : "";
3797
4101
  const originalDbContainerState = inspectContainer(dbContainer);
3798
4102
  const dbPortBindings = portPublishArgs(originalDbContainerState);
3799
4103
  runDocker(["stop", dbContainer]);
3800
4104
  runDocker(["rename", dbContainer, originalDbContainer]);
3801
4105
  startProxyContainer(projectId, network, dbContainer, parsedBranchUrl, []);
4106
+ console.log("Checking branch database connection...");
4107
+ waitForBranchProxy(originalDbContainer, dbContainer, network, branchUser, branchPassword);
4108
+ console.log("Copying local Supabase Storage migration state...");
3802
4109
  copyLocalStorageMigrationsToBranch(
3803
4110
  storageMigrationsDump,
3804
4111
  originalDbContainer,
@@ -3807,19 +4114,17 @@ async function linkSupabaseAction(branchName, options = {}) {
3807
4114
  branchUser,
3808
4115
  branchPassword
3809
4116
  );
3810
- for (const servicePrefix of SERVICE_SUFFIXES) {
3811
- const service = recreateServiceForBranch(
3812
- servicePrefix,
3813
- projectId,
3814
- network,
4117
+ if (realtimeMigrationsDump) {
4118
+ console.log("Copying local Supabase Realtime migration state...");
4119
+ copyLocalRealtimeMigrationsToBranch(
4120
+ realtimeMigrationsDump,
4121
+ originalDbContainer,
3815
4122
  dbContainer,
3816
- branchUser,
4123
+ network,
3817
4124
  branchPassword
3818
4125
  );
3819
- if (service) {
3820
- createdServices.push(service);
3821
- }
3822
4126
  }
4127
+ console.log("Applying Supabase Realtime compatibility...");
3823
4128
  applySupabaseRealtimeCompatibility(
3824
4129
  projectId,
3825
4130
  originalDbContainer,
@@ -3829,6 +4134,27 @@ async function linkSupabaseAction(branchName, options = {}) {
3829
4134
  );
3830
4135
  runDocker(["rm", "-f", dbContainer]);
3831
4136
  startProxyContainer(projectId, network, dbContainer, parsedBranchUrl, dbPortBindings);
4137
+ console.log("Starting linked Supabase database proxy...");
4138
+ waitForBranchProxyWithClientImage(
4139
+ originalDbContainerState.Config.Image,
4140
+ network,
4141
+ dbContainer,
4142
+ branchUser,
4143
+ branchPassword
4144
+ );
4145
+ for (const servicePrefix of SERVICE_SUFFIXES) {
4146
+ const service = recreateServiceForBranch(
4147
+ servicePrefix,
4148
+ projectId,
4149
+ network,
4150
+ dbContainer,
4151
+ branchUser,
4152
+ branchPassword
4153
+ );
4154
+ if (service) {
4155
+ createdServices.push(service);
4156
+ }
4157
+ }
3832
4158
  const kong = containerName("kong", projectId);
3833
4159
  if (dockerExists(kong)) {
3834
4160
  runDocker(["restart", kong]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ardent-cli",
3
- "version": "0.0.38",
3
+ "version": "0.0.40",
4
4
  "description": "Git for Data infrastructure",
5
5
  "type": "module",
6
6
  "bin": {