Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .superset/lib/setup/main.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ setup_main() {
step_failed "Seed local DB"
fi

# Step 5: Seed auth token into superset-dev-data/
# Step 5: Seed host-service DBs into superset-dev-data/host/
if ! step_seed_host_dbs; then
step_failed "Seed host-service DBs"
fi

# Step 6: Seed auth token into superset-dev-data/
if ! step_seed_auth_token; then
step_failed "Seed auth token"
fi
Expand Down
90 changes: 90 additions & 0 deletions .superset/lib/setup/steps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,96 @@ step_seed_auth_token() {
return 0
}

step_seed_host_dbs() {
echo "🛰️ Seeding host-service DBs into superset-dev-data/host/..."

local source_root="$HOME/.superset/host"
local dev_data_dir="superset-dev-data"
local dest_root="$dev_data_dir/host"
local force_overwrite="$FORCE_OVERWRITE_DATA"

if [ ! -d "$source_root" ]; then
warn "No host-service DBs found at $source_root — skipping (host-service will create fresh DBs per org)"
step_skipped "Seed host-service DBs (no source dir)"
return 0
fi

local org_dirs=()
for org_dir in "$source_root"/*/; do
[ -d "$org_dir" ] || continue
local org_id
org_id="$(basename "$org_dir")"
if [ -f "${org_dir}host.db" ]; then
org_dirs+=("$org_id")
fi
done

if [ ${#org_dirs[@]} -eq 0 ]; then
warn "No host.db files under $source_root — skipping"
step_skipped "Seed host-service DBs (no host.db files)"
return 0
fi

mkdir -p "$dest_root"
chmod 700 "$dev_data_dir" "$dest_root"

local seeded=0
local skipped=0
for org_id in "${org_dirs[@]}"; do
local source_db="$source_root/$org_id/host.db"
local dest_org_dir="$dest_root/$org_id"
local dest_db="$dest_org_dir/host.db"

if [ -f "$dest_db" ] && [ "$force_overwrite" != "1" ]; then
warn "Host DB already exists at $dest_db — skipping (use -f/--force)"
skipped=$((skipped + 1))
continue
fi

mkdir -p "$dest_org_dir"
chmod 700 "$dest_org_dir"

# Clear stale WAL siblings when overwriting so we don't mix old WAL
# data with a freshly-copied DB (their page pointers won't match).
if [ "$force_overwrite" = "1" ]; then
rm -f "$dest_db" "${dest_db}-shm" "${dest_db}-wal"
fi

# Copy all SQLite files so WAL data isn't lost when source is held open.
local copy_failed=0
for ext in "" "-shm" "-wal"; do
local source_file="${source_db}${ext}"
local dest_file="${dest_db}${ext}"

if [ -f "$source_file" ]; then
if ! cp "$source_file" "$dest_file"; then
error "Failed to copy $source_file to $dest_file"
copy_failed=1
break
fi
chmod 600 "$dest_file"
fi
done

if [ "$copy_failed" = "1" ]; then
# A lone host.db without its -wal/-shm siblings would make the next
# non-force run think this org is already seeded and skip it.
rm -f "$dest_db" "${dest_db}-shm" "${dest_db}-wal"
return 1
fi

# Checkpoint the copy's WAL (no lock contention since nothing else has it open).
if command -v sqlite3 &> /dev/null; then
sqlite3 "$dest_db" "PRAGMA wal_checkpoint(TRUNCATE);" &> /dev/null || true
fi

seeded=$((seeded + 1))
done

success "Host-service DBs seeded ($seeded copied, $skipped skipped) from $source_root"
return 0
}

step_seed_local_db() {
echo "💾 Seeding local DB into superset-dev-data/..."

Expand Down
83 changes: 83 additions & 0 deletions apps/desktop/src/lib/trpc/routers/workspaces/utils/git.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,89 @@ describe("createWorktree hook tolerance", () => {
createWorktree(repoPath, "feature/existing-path", worktreePath, "HEAD"),
).rejects.toThrow("already exists");
}, 10_000);

test("works with remote-tracking ref as start point (no-track prevents upstream)", async () => {
// Set up a "remote" repo with a commit, then clone it so we have origin/<branch> refs
const originPath = join(TEST_DIR, "worktree-notrack-origin");
mkdirSync(originPath, { recursive: true });
execSync("git init -b main", { cwd: originPath, stdio: "ignore" });
execSync("git config user.email 'test@test.com'", {
cwd: originPath,
stdio: "ignore",
});
execSync("git config user.name 'Test'", {
cwd: originPath,
stdio: "ignore",
});
writeFileSync(join(originPath, "README.md"), "# test\n");
execSync("git add . && git commit -m 'init'", {
cwd: originPath,
stdio: "ignore",
});

const clonePath = join(TEST_DIR, "worktree-notrack-clone");
execSync(`git clone "${originPath}" "${clonePath}"`, {
stdio: "ignore",
});
execSync("git config user.email 'test@test.com'", {
cwd: clonePath,
stdio: "ignore",
});
execSync("git config user.name 'Test'", {
cwd: clonePath,
stdio: "ignore",
});

const worktreePath = join(TEST_DIR, "worktree-notrack-wt");
await createWorktree(
clonePath,
"feature/no-track-test",
worktreePath,
"origin/main",
);

expect(existsSync(worktreePath)).toBe(true);

// Verify the new branch does NOT track origin/main
const trackingResult = execSync(
"git config --get branch.feature/no-track-test.remote 2>&1 || true",
{ cwd: worktreePath },
)
.toString()
.trim();
expect(trackingResult).toBe("");
}, 15_000);

test("works with a branch name containing slashes as start point", async () => {
// Reproduces #3448: createWorktree previously appended ^{commit} to the
// start point, which can fail with "fatal: invalid reference" when the ref
// is not locally resolvable with that suffix. Using --no-track avoids this.
const repoPath = createTestRepo("worktree-slash-branch");
seedCommit(repoPath);

// Create a branch with slashes (like feat/workstreams-view)
execSync("git checkout -b feat/workstreams-view", {
cwd: repoPath,
stdio: "ignore",
});
execSync("git checkout -", { cwd: repoPath, stdio: "ignore" });

const worktreePath = join(TEST_DIR, "worktree-slash-branch-wt");
await createWorktree(
repoPath,
"feature/new-workspace",
worktreePath,
"feat/workstreams-view",
);

expect(existsSync(worktreePath)).toBe(true);
const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", {
cwd: worktreePath,
})
.toString()
.trim();
expect(currentBranch).toBe("feature/new-workspace");
}, 10_000);
});

describe("getCurrentBranch", () => {
Expand Down
10 changes: 5 additions & 5 deletions apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -585,13 +585,13 @@ export async function createWorktree(
mainRepoPath,
"worktree",
"add",
worktreePath,
// --no-track prevents the new branch from tracking the remote ref
// (e.g. origin/main); push.autoSetupRemote handles first-push tracking.
"--no-track",
"-b",
branch,
// Append ^{commit} to force Git to treat the startPoint as a commit,
// not a branch ref. This prevents implicit upstream tracking when
// creating a new branch from a remote branch like origin/main.
`${startPoint}^{commit}`,
worktreePath,
startPoint,
],
worktreePath,
});
Expand Down
6 changes: 2 additions & 4 deletions apps/desktop/src/main/host-service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
resolveTerminalBaseEnv,
} from "@superset/host-service/terminal-env";
import { connectRelay } from "@superset/host-service/tunnel";
import { removeManifest, writeManifest } from "main/lib/host-service-manifest";
import { writeManifest } from "main/lib/host-service-manifest";
import { env } from "./env";

async function main(): Promise<void> {
Expand Down Expand Up @@ -81,10 +81,8 @@ async function main(): Promise<void> {
);
injectWebSocket(server);

// Manifest lifecycle belongs to the coordinator, not the child.
const shutdown = () => {
if (env.ORGANIZATION_ID) {
removeManifest(env.ORGANIZATION_ID);
}
server.close();
process.exit(0);
};
Expand Down
Loading
Loading