Skip to content

Commit 34508a6

Browse files
authored
Merge pull request Sofie-Automation#1641 from rjmunro/rjmunro/dev-database-switcher
Rjmunro/dev database switcher
2 parents f2f0823 + d98a511 commit 34508a6

2 files changed

Lines changed: 155 additions & 1 deletion

File tree

scripts/lib.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,50 @@
11
const args = process.argv.slice(2);
22

3+
// Check for --help
4+
if (args.indexOf("--help") >= 0 || args.indexOf("-h") >= 0) {
5+
console.log(`
6+
Sofie Core Development Mode
7+
8+
Usage: yarn dev [options]
9+
yarn start [options]
10+
11+
Note: 'yarn start' runs install + build + dev, while 'yarn dev' just runs dev mode.
12+
All options work with both commands.
13+
14+
Options:
15+
--help, -h Show this help message
16+
--ui-only Only watch and build UI packages (skip job-worker, gateways)
17+
--inspect-meteor Run Meteor with Node.js inspector enabled
18+
--verbose Enable verbose logging
19+
--db=<name> Use a named database directory (e.g., --db=demo)
20+
Creates meteor/.meteor/local/db.<name> and switches to it with a symlink
21+
Original database is backed up to db.default on first use
22+
Run without --db to use the currently active database
23+
--db-list List all available database directories and show which is active
24+
25+
Examples:
26+
yarn start # Install, build, then run in dev mode
27+
yarn dev # Run in normal dev mode (requires prior build)
28+
yarn dev --db-list # List all available databases
29+
yarn dev --db=testing # Use a separate database for testing
30+
yarn dev --db=demo # Switch to demo database
31+
yarn dev --ui-only # Only watch UI, skip backend packages
32+
yarn dev --inspect-meteor # Debug Meteor with inspector
33+
yarn start --db=demo # Install, build, and run with demo database
34+
`);
35+
process.exit(0);
36+
}
37+
38+
// Parse --db=name option
39+
const dbArg = args.find(arg => arg.startsWith('--db='));
40+
const dbName = dbArg ? dbArg.split('=')[1] : null;
41+
342
const config = {
443
uiOnly: args.indexOf("--ui-only") >= 0 || false,
544
inspectMeteor: args.indexOf("--inspect-meteor") >= 0 || false,
645
verbose: args.indexOf("--verbose") >= 0 || false,
46+
dbName: dbName,
47+
dbList: args.indexOf("--db-list") >= 0 || false,
748
};
849

950
module.exports = {

scripts/run.mjs

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import process from "process";
22
import fs from "fs";
3+
import path from "path";
34
import concurrently from "concurrently";
45
import { config } from "./lib.js";
56

@@ -71,8 +72,120 @@ function hr() {
7172
return "─".repeat(process.stdout.columns ?? 40);
7273
}
7374

75+
function listDatabases() {
76+
const meteorLocalDir = path.join('meteor', '.meteor', 'local');
77+
const dbLink = path.join(meteorLocalDir, 'db');
78+
79+
if (!fs.existsSync(meteorLocalDir)) {
80+
console.log('No databases found (meteor/.meteor/local does not exist yet)');
81+
return;
82+
}
83+
84+
// Get current database
85+
let currentDb = null;
86+
if (fs.existsSync(dbLink)) {
87+
const stats = fs.lstatSync(dbLink);
88+
if (stats.isSymbolicLink()) {
89+
const target = fs.readlinkSync(dbLink);
90+
const match = target.match(/^db\.(.+)$/);
91+
if (match) {
92+
currentDb = match[1];
93+
}
94+
} else {
95+
currentDb = '(unnamed - real directory)';
96+
}
97+
}
98+
99+
// List all db.* directories
100+
const files = fs.readdirSync(meteorLocalDir);
101+
const dbDirs = files
102+
.filter(file => file.startsWith('db.') && fs.lstatSync(path.join(meteorLocalDir, file)).isDirectory())
103+
.map(file => file.substring(3));
104+
105+
console.log('\nAvailable databases:');
106+
if (dbDirs.length === 0) {
107+
console.log(' (none found)');
108+
} else {
109+
dbDirs.sort().forEach(db => {
110+
const marker = db === currentDb ? ' ← current' : '';
111+
console.log(` ${db}${marker}`);
112+
});
113+
}
114+
115+
if (currentDb && !dbDirs.includes(currentDb)) {
116+
console.log(`\nCurrent: ${currentDb}`);
117+
}
118+
console.log('');
119+
}
120+
121+
function switchDatabase(dbName) {
122+
const meteorLocalDir = path.join('meteor', '.meteor', 'local');
123+
const dbLink = path.join(meteorLocalDir, 'db');
124+
const dbTarget = path.join(meteorLocalDir, `db.${dbName}`);
125+
126+
// Check if we're already using this database
127+
if (fs.existsSync(dbLink)) {
128+
const stats = fs.lstatSync(dbLink);
129+
if (stats.isSymbolicLink()) {
130+
const currentTarget = fs.readlinkSync(dbLink);
131+
if (currentTarget === `db.${dbName}`) {
132+
console.log(`✓ Already using database: ${dbName}`);
133+
return;
134+
}
135+
}
136+
}
137+
138+
// Create target directory if it doesn't exist
139+
if (!fs.existsSync(dbTarget)) {
140+
console.log(`Creating new database directory: ${dbName}`);
141+
fs.mkdirSync(dbTarget, { recursive: true });
142+
}
143+
144+
// Remove existing db link/directory
145+
if (fs.existsSync(dbLink)) {
146+
const stats = fs.lstatSync(dbLink);
147+
if (stats.isSymbolicLink()) {
148+
fs.unlinkSync(dbLink);
149+
} else {
150+
// It's a real directory - back it up with timestamp
151+
const defaultDb = path.join(meteorLocalDir, 'db.default');
152+
if (!fs.existsSync(defaultDb)) {
153+
console.log(`Backing up existing database to: default`);
154+
fs.renameSync(dbLink, defaultDb);
155+
} else {
156+
// Default already exists, create timestamped backup instead of deleting
157+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19);
158+
let backupName = path.join(meteorLocalDir, `db.backup.${timestamp}`);
159+
// Ensure unique backup name
160+
let suffix = 0;
161+
while (fs.existsSync(backupName)) {
162+
suffix++;
163+
backupName = path.join(meteorLocalDir, `db.backup.${timestamp}.${suffix}`);
164+
}
165+
console.log(`Backing up existing database to: ${path.basename(backupName)}`);
166+
fs.renameSync(dbLink, backupName);
167+
}
168+
}
169+
}
170+
171+
// Create symlink to target database
172+
fs.symlinkSync(`db.${dbName}`, dbLink);
173+
console.log(`✓ Switched to database: ${dbName}`);
174+
}
175+
74176
try {
75-
// Note: This scricpt assumes that install-and-build.mjs has been run before
177+
// Note: This script assumes that install-and-build.mjs has been run before
178+
179+
// List databases if requested
180+
if (config.dbList) {
181+
listDatabases();
182+
process.exit(0);
183+
}
184+
185+
// Switch database if requested
186+
if (config.dbName) {
187+
switchDatabase(config.dbName);
188+
}
76189

77190
// The main watching execution
78191
console.log(hr());

0 commit comments

Comments
 (0)