ardent-cli 0.0.38 → 0.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +253 -19
- 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
|
|
@@ -2944,6 +2952,7 @@ var ORIGINAL_SUFFIX = "-ardent-original";
|
|
|
2944
2952
|
var SERVICE_SUFFIXES = ["rest", "auth", "storage", "realtime", "pg_meta"];
|
|
2945
2953
|
var DOCKER_HOST_GATEWAY = "host.docker.internal";
|
|
2946
2954
|
var REALTIME_LIST_CHANGES_MIGRATION_VERSION = "20230328144023";
|
|
2955
|
+
var MIGRATION_IMPORT_TIMEOUT_MS = 15e3;
|
|
2947
2956
|
var SENSITIVE_DOCKER_ENV_KEYS = /* @__PURE__ */ new Set([
|
|
2948
2957
|
"DATABASE_URL",
|
|
2949
2958
|
"DB_PASSWORD",
|
|
@@ -3521,19 +3530,68 @@ function dumpLocalStorageMigrations(dbContainer) {
|
|
|
3521
3530
|
"--no-privileges"
|
|
3522
3531
|
]);
|
|
3523
3532
|
}
|
|
3533
|
+
function dumpLocalRealtimeMigrations(dbContainer) {
|
|
3534
|
+
return runDocker([
|
|
3535
|
+
"exec",
|
|
3536
|
+
dbContainer,
|
|
3537
|
+
"pg_dump",
|
|
3538
|
+
"-U",
|
|
3539
|
+
"postgres",
|
|
3540
|
+
"-d",
|
|
3541
|
+
"postgres",
|
|
3542
|
+
"--data-only",
|
|
3543
|
+
"--table=realtime.schema_migrations",
|
|
3544
|
+
"--column-inserts",
|
|
3545
|
+
"--no-owner",
|
|
3546
|
+
"--no-privileges"
|
|
3547
|
+
]);
|
|
3548
|
+
}
|
|
3524
3549
|
function storageMigrationsImportSql(dumpSql) {
|
|
3525
3550
|
const tempTable = "pg_temp.ardent_storage_migrations_import";
|
|
3526
|
-
const tempDumpSql = dumpSql
|
|
3551
|
+
const tempDumpSql = retargetPgDumpInserts(dumpSql, "storage", "migrations", tempTable);
|
|
3527
3552
|
return `
|
|
3553
|
+
SET lock_timeout = '2s';
|
|
3554
|
+
SET statement_timeout = '10s';
|
|
3528
3555
|
BEGIN;
|
|
3529
3556
|
CREATE TEMP TABLE ardent_storage_migrations_import (LIKE storage.migrations INCLUDING DEFAULTS);
|
|
3530
3557
|
${tempDumpSql}
|
|
3531
3558
|
INSERT INTO storage.migrations
|
|
3532
3559
|
SELECT * FROM ${tempTable}
|
|
3533
3560
|
ON CONFLICT DO NOTHING;
|
|
3561
|
+
COMMIT;
|
|
3562
|
+
`;
|
|
3563
|
+
}
|
|
3564
|
+
function realtimeMigrationsImportSql(dumpSql) {
|
|
3565
|
+
const tempTable = "pg_temp.ardent_realtime_migrations_import";
|
|
3566
|
+
const tempDumpSql = retargetPgDumpInserts(dumpSql, "realtime", "schema_migrations", tempTable);
|
|
3567
|
+
return `
|
|
3568
|
+
SET lock_timeout = '2s';
|
|
3569
|
+
SET statement_timeout = '10s';
|
|
3570
|
+
BEGIN;
|
|
3571
|
+
CREATE SCHEMA IF NOT EXISTS realtime;
|
|
3572
|
+
CREATE TABLE IF NOT EXISTS realtime.schema_migrations (
|
|
3573
|
+
version bigint PRIMARY KEY,
|
|
3574
|
+
inserted_at timestamp(0) without time zone
|
|
3575
|
+
);
|
|
3576
|
+
CREATE TEMP TABLE ardent_realtime_migrations_import (LIKE realtime.schema_migrations INCLUDING DEFAULTS);
|
|
3577
|
+
${tempDumpSql}
|
|
3578
|
+
INSERT INTO realtime.schema_migrations
|
|
3579
|
+
SELECT * FROM ${tempTable}
|
|
3580
|
+
ON CONFLICT DO NOTHING;
|
|
3534
3581
|
COMMIT;
|
|
3535
3582
|
`;
|
|
3536
3583
|
}
|
|
3584
|
+
function retargetPgDumpInserts(dumpSql, schema, table, tempTable) {
|
|
3585
|
+
const insertPattern = new RegExp(
|
|
3586
|
+
`INSERT INTO (?:${schema}\\.${table}|"${schema}"\\."${table}")`,
|
|
3587
|
+
"g"
|
|
3588
|
+
);
|
|
3589
|
+
const tempDumpSql = dumpSql.replace(insertPattern, `INSERT INTO ${tempTable}`);
|
|
3590
|
+
if (/\bINSERT INTO\b/.test(dumpSql) && tempDumpSql === dumpSql) {
|
|
3591
|
+
throw new Error(`Could not rewrite pg_dump INSERT statements for ${schema}.${table}.`);
|
|
3592
|
+
}
|
|
3593
|
+
return tempDumpSql;
|
|
3594
|
+
}
|
|
3537
3595
|
function waitForOriginalDb(containerName2) {
|
|
3538
3596
|
const deadline = Date.now() + 3e4;
|
|
3539
3597
|
while (Date.now() < deadline) {
|
|
@@ -3546,6 +3604,81 @@ function waitForOriginalDb(containerName2) {
|
|
|
3546
3604
|
}
|
|
3547
3605
|
throw new Error(`Timed out waiting for ${containerName2} to accept local PostgreSQL connections.`);
|
|
3548
3606
|
}
|
|
3607
|
+
function waitForBranchProxy(originalDbContainer, dbContainer, network, branchUser, branchPassword) {
|
|
3608
|
+
runDocker(["start", originalDbContainer]);
|
|
3609
|
+
try {
|
|
3610
|
+
waitForOriginalDb(originalDbContainer);
|
|
3611
|
+
const dbContainerIpAddress = containerIpAddress(dbContainer, network);
|
|
3612
|
+
const deadline = Date.now() + 6e4;
|
|
3613
|
+
let lastError = "";
|
|
3614
|
+
while (Date.now() < deadline) {
|
|
3615
|
+
try {
|
|
3616
|
+
runDocker([
|
|
3617
|
+
"exec",
|
|
3618
|
+
"-e",
|
|
3619
|
+
`PGPASSWORD=${branchPassword}`,
|
|
3620
|
+
originalDbContainer,
|
|
3621
|
+
"psql",
|
|
3622
|
+
"-h",
|
|
3623
|
+
dbContainerIpAddress,
|
|
3624
|
+
"-p",
|
|
3625
|
+
"5432",
|
|
3626
|
+
"-U",
|
|
3627
|
+
branchUser,
|
|
3628
|
+
"-d",
|
|
3629
|
+
"postgres",
|
|
3630
|
+
"-v",
|
|
3631
|
+
"ON_ERROR_STOP=1",
|
|
3632
|
+
"-c",
|
|
3633
|
+
"SELECT 1"
|
|
3634
|
+
]);
|
|
3635
|
+
return;
|
|
3636
|
+
} catch (error) {
|
|
3637
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
3638
|
+
execFileSync("sleep", ["1"]);
|
|
3639
|
+
}
|
|
3640
|
+
}
|
|
3641
|
+
throw new Error(`Timed out waiting for branch DB proxy to accept connections: ${lastError}`);
|
|
3642
|
+
} finally {
|
|
3643
|
+
runDocker(["stop", originalDbContainer]);
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3646
|
+
function waitForBranchProxyWithClientImage(clientImage, network, dbContainer, branchUser, branchPassword) {
|
|
3647
|
+
const deadline = Date.now() + 6e4;
|
|
3648
|
+
let lastError = "";
|
|
3649
|
+
while (Date.now() < deadline) {
|
|
3650
|
+
try {
|
|
3651
|
+
runDocker([
|
|
3652
|
+
"run",
|
|
3653
|
+
"--rm",
|
|
3654
|
+
"--network",
|
|
3655
|
+
network,
|
|
3656
|
+
"--entrypoint",
|
|
3657
|
+
"psql",
|
|
3658
|
+
"-e",
|
|
3659
|
+
`PGPASSWORD=${branchPassword}`,
|
|
3660
|
+
clientImage,
|
|
3661
|
+
"-h",
|
|
3662
|
+
dbContainer,
|
|
3663
|
+
"-p",
|
|
3664
|
+
"5432",
|
|
3665
|
+
"-U",
|
|
3666
|
+
branchUser,
|
|
3667
|
+
"-d",
|
|
3668
|
+
"postgres",
|
|
3669
|
+
"-v",
|
|
3670
|
+
"ON_ERROR_STOP=1",
|
|
3671
|
+
"-c",
|
|
3672
|
+
"SELECT 1"
|
|
3673
|
+
]);
|
|
3674
|
+
return;
|
|
3675
|
+
} catch (error) {
|
|
3676
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
3677
|
+
execFileSync("sleep", ["1"]);
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
throw new Error(`Timed out waiting for final branch DB proxy to accept connections: ${lastError}`);
|
|
3681
|
+
}
|
|
3549
3682
|
function copyLocalStorageMigrationsToBranch(dumpSql, originalDbContainer, dbContainer, network, branchUser, branchPassword) {
|
|
3550
3683
|
runDocker(["start", originalDbContainer]);
|
|
3551
3684
|
try {
|
|
@@ -3570,7 +3703,39 @@ function copyLocalStorageMigrationsToBranch(dumpSql, originalDbContainer, dbCont
|
|
|
3570
3703
|
"-v",
|
|
3571
3704
|
"ON_ERROR_STOP=1"
|
|
3572
3705
|
],
|
|
3573
|
-
storageMigrationsImportSql(dumpSql)
|
|
3706
|
+
storageMigrationsImportSql(dumpSql),
|
|
3707
|
+
{ timeoutMs: MIGRATION_IMPORT_TIMEOUT_MS }
|
|
3708
|
+
);
|
|
3709
|
+
} finally {
|
|
3710
|
+
runDocker(["stop", originalDbContainer]);
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
function copyLocalRealtimeMigrationsToBranch(dumpSql, originalDbContainer, dbContainer, network, branchPassword) {
|
|
3714
|
+
runDocker(["start", originalDbContainer]);
|
|
3715
|
+
try {
|
|
3716
|
+
waitForOriginalDb(originalDbContainer);
|
|
3717
|
+
const dbContainerIpAddress = containerIpAddress(dbContainer, network);
|
|
3718
|
+
runDockerWithInput(
|
|
3719
|
+
[
|
|
3720
|
+
"exec",
|
|
3721
|
+
"-i",
|
|
3722
|
+
"-e",
|
|
3723
|
+
`PGPASSWORD=${branchPassword}`,
|
|
3724
|
+
originalDbContainer,
|
|
3725
|
+
"psql",
|
|
3726
|
+
"-h",
|
|
3727
|
+
dbContainerIpAddress,
|
|
3728
|
+
"-p",
|
|
3729
|
+
"5432",
|
|
3730
|
+
"-U",
|
|
3731
|
+
"supabase_admin",
|
|
3732
|
+
"-d",
|
|
3733
|
+
"postgres",
|
|
3734
|
+
"-v",
|
|
3735
|
+
"ON_ERROR_STOP=1"
|
|
3736
|
+
],
|
|
3737
|
+
realtimeMigrationsImportSql(dumpSql),
|
|
3738
|
+
{ timeoutMs: MIGRATION_IMPORT_TIMEOUT_MS }
|
|
3574
3739
|
);
|
|
3575
3740
|
} finally {
|
|
3576
3741
|
runDocker(["stop", originalDbContainer]);
|
|
@@ -3691,7 +3856,6 @@ function applySupabaseRealtimeCompatibility(projectId, originalDbContainer, dbCo
|
|
|
3691
3856
|
supabaseRealtimeCompatibilitySql(),
|
|
3692
3857
|
{ timeoutMs: 7e3 }
|
|
3693
3858
|
);
|
|
3694
|
-
runDocker(["restart", realtimeContainer]);
|
|
3695
3859
|
return;
|
|
3696
3860
|
} catch (error) {
|
|
3697
3861
|
lastError = error instanceof Error ? error.message : String(error);
|
|
@@ -3713,6 +3877,42 @@ function kongApiUrl(projectId) {
|
|
|
3713
3877
|
const port = first?.split(":").pop();
|
|
3714
3878
|
return port ? `http://127.0.0.1:${port}` : void 0;
|
|
3715
3879
|
}
|
|
3880
|
+
function requireLinkedServiceContainersRunning(projectId) {
|
|
3881
|
+
const stoppedServices = [];
|
|
3882
|
+
for (const servicePrefix of SERVICE_SUFFIXES) {
|
|
3883
|
+
const name = containerName(servicePrefix, projectId);
|
|
3884
|
+
if (!dockerExists(name)) {
|
|
3885
|
+
continue;
|
|
3886
|
+
}
|
|
3887
|
+
const state = runDocker([
|
|
3888
|
+
"inspect",
|
|
3889
|
+
"-f",
|
|
3890
|
+
"{{.State.Status}}",
|
|
3891
|
+
name
|
|
3892
|
+
]);
|
|
3893
|
+
if (state !== "running") {
|
|
3894
|
+
stoppedServices.push(`${name}=${state}`);
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
if (stoppedServices.length > 0) {
|
|
3898
|
+
throw new Error(`Linked Supabase service container is not running: ${stoppedServices.join(", ")}`);
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
async function closeHealthResponse(response) {
|
|
3902
|
+
try {
|
|
3903
|
+
await response.body?.cancel();
|
|
3904
|
+
} catch {
|
|
3905
|
+
}
|
|
3906
|
+
}
|
|
3907
|
+
async function fetchHealthCheck(url) {
|
|
3908
|
+
const controller = new AbortController();
|
|
3909
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
3910
|
+
try {
|
|
3911
|
+
return await fetch(url, { signal: controller.signal });
|
|
3912
|
+
} finally {
|
|
3913
|
+
clearTimeout(timeout);
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3716
3916
|
async function waitForHealth(projectId) {
|
|
3717
3917
|
const apiUrl = kongApiUrl(projectId);
|
|
3718
3918
|
if (!apiUrl) {
|
|
@@ -3722,12 +3922,22 @@ async function waitForHealth(projectId) {
|
|
|
3722
3922
|
let lastError = "";
|
|
3723
3923
|
while (Date.now() < deadline) {
|
|
3724
3924
|
try {
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3925
|
+
requireLinkedServiceContainersRunning(projectId);
|
|
3926
|
+
let authResponse;
|
|
3927
|
+
let restResponse;
|
|
3928
|
+
try {
|
|
3929
|
+
authResponse = await fetchHealthCheck(`${apiUrl}/auth/v1/health`);
|
|
3930
|
+
restResponse = await fetchHealthCheck(`${apiUrl}/rest/v1/`);
|
|
3931
|
+
if (authResponse.ok && restResponse.ok) {
|
|
3932
|
+
return;
|
|
3933
|
+
}
|
|
3934
|
+
lastError = `auth=${authResponse.status}, rest=${restResponse.status}`;
|
|
3935
|
+
} finally {
|
|
3936
|
+
await Promise.all([
|
|
3937
|
+
authResponse ? closeHealthResponse(authResponse) : Promise.resolve(),
|
|
3938
|
+
restResponse ? closeHealthResponse(restResponse) : Promise.resolve()
|
|
3939
|
+
]);
|
|
3729
3940
|
}
|
|
3730
|
-
lastError = `auth=${authResponse.status}, rest=${restResponse.status}`;
|
|
3731
3941
|
} catch (error) {
|
|
3732
3942
|
lastError = error instanceof Error ? error.message : String(error);
|
|
3733
3943
|
}
|
|
@@ -3794,11 +4004,16 @@ async function linkSupabaseAction(branchName, options = {}) {
|
|
|
3794
4004
|
}
|
|
3795
4005
|
console.log(`Linking Supabase project ${projectId} to branch ${branch.name}...`);
|
|
3796
4006
|
const storageMigrationsDump = dumpLocalStorageMigrations(dbContainer);
|
|
4007
|
+
const realtimeContainer = containerName("realtime", projectId);
|
|
4008
|
+
const realtimeMigrationsDump = dockerExists(realtimeContainer) ? dumpLocalRealtimeMigrations(dbContainer) : "";
|
|
3797
4009
|
const originalDbContainerState = inspectContainer(dbContainer);
|
|
3798
4010
|
const dbPortBindings = portPublishArgs(originalDbContainerState);
|
|
3799
4011
|
runDocker(["stop", dbContainer]);
|
|
3800
4012
|
runDocker(["rename", dbContainer, originalDbContainer]);
|
|
3801
4013
|
startProxyContainer(projectId, network, dbContainer, parsedBranchUrl, []);
|
|
4014
|
+
console.log("Checking branch database connection...");
|
|
4015
|
+
waitForBranchProxy(originalDbContainer, dbContainer, network, branchUser, branchPassword);
|
|
4016
|
+
console.log("Copying local Supabase Storage migration state...");
|
|
3802
4017
|
copyLocalStorageMigrationsToBranch(
|
|
3803
4018
|
storageMigrationsDump,
|
|
3804
4019
|
originalDbContainer,
|
|
@@ -3807,19 +4022,17 @@ async function linkSupabaseAction(branchName, options = {}) {
|
|
|
3807
4022
|
branchUser,
|
|
3808
4023
|
branchPassword
|
|
3809
4024
|
);
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
4025
|
+
if (realtimeMigrationsDump) {
|
|
4026
|
+
console.log("Copying local Supabase Realtime migration state...");
|
|
4027
|
+
copyLocalRealtimeMigrationsToBranch(
|
|
4028
|
+
realtimeMigrationsDump,
|
|
4029
|
+
originalDbContainer,
|
|
3815
4030
|
dbContainer,
|
|
3816
|
-
|
|
4031
|
+
network,
|
|
3817
4032
|
branchPassword
|
|
3818
4033
|
);
|
|
3819
|
-
if (service) {
|
|
3820
|
-
createdServices.push(service);
|
|
3821
|
-
}
|
|
3822
4034
|
}
|
|
4035
|
+
console.log("Applying Supabase Realtime compatibility...");
|
|
3823
4036
|
applySupabaseRealtimeCompatibility(
|
|
3824
4037
|
projectId,
|
|
3825
4038
|
originalDbContainer,
|
|
@@ -3829,6 +4042,27 @@ async function linkSupabaseAction(branchName, options = {}) {
|
|
|
3829
4042
|
);
|
|
3830
4043
|
runDocker(["rm", "-f", dbContainer]);
|
|
3831
4044
|
startProxyContainer(projectId, network, dbContainer, parsedBranchUrl, dbPortBindings);
|
|
4045
|
+
console.log("Starting linked Supabase database proxy...");
|
|
4046
|
+
waitForBranchProxyWithClientImage(
|
|
4047
|
+
originalDbContainerState.Config.Image,
|
|
4048
|
+
network,
|
|
4049
|
+
dbContainer,
|
|
4050
|
+
branchUser,
|
|
4051
|
+
branchPassword
|
|
4052
|
+
);
|
|
4053
|
+
for (const servicePrefix of SERVICE_SUFFIXES) {
|
|
4054
|
+
const service = recreateServiceForBranch(
|
|
4055
|
+
servicePrefix,
|
|
4056
|
+
projectId,
|
|
4057
|
+
network,
|
|
4058
|
+
dbContainer,
|
|
4059
|
+
branchUser,
|
|
4060
|
+
branchPassword
|
|
4061
|
+
);
|
|
4062
|
+
if (service) {
|
|
4063
|
+
createdServices.push(service);
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
3832
4066
|
const kong = containerName("kong", projectId);
|
|
3833
4067
|
if (dockerExists(kong)) {
|
|
3834
4068
|
runDocker(["restart", kong]);
|