Skip to content
Draft
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
19 changes: 13 additions & 6 deletions DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ Follow these instructions to start up Sofie Core in development mode. (For produ
git clone -b main https://github.com/Sofie-Automation/sofie-core.git
cd sofie-core
yarn
yarn start
yarn dev # Install and build all packages, then start in dev mode with file watching

# Or

yarn
yarn build # Install and build all packages
yarn start # Start Sofie Core (no file watching)
```

> 💡 First startup may take a while, especially on Windows. To speed things up, consider adding `%LOCALAPPDATA%\.meteor` and the directory where you cloned `server-core` to your Windows Defender virus protection exclusions.
Expand Down Expand Up @@ -60,22 +66,23 @@ The Sofie ui (served by Vite) can be accessed at `http://localhost:3005`. The me
4. Start development mode

```bash
yarn dev
yarn dev # Install and build all packages, then start in dev mode with file watching
```

5. In another window, start the playout-gateway. You will need to manually restart this upon making changes

```bash
cd sofie-core/packages/playout-gateway
yarn buildstart
yarn build # If needed
yarn start
```

### Lowering memory, CPU footprint in development

If you find yourself in a situation where running Sofie in development mode is too heavy, but you're not planning on modifying any of the low-level packages in the `packages` directory, you may want to run Sofie in the _UI-only mode_, in which only meteor and the ui will be rebuilt and type-checked on modification:
If you find yourself in a situation where running Sofie in development mode is too heavy, but you're not planning on modifying any of the low-level packages in the `packages` directory, you may want to run Sofie without file watching and type-checking.

```bash
yarn dev --ui-only
yarn start # Start Sofie Core (no file watching, requires a `yarn build` first)
```

### Dealing with Strange Errors
Expand All @@ -84,7 +91,7 @@ If you get any strange errors (such as the application crashing, "Unable to reso

```bash
yarn reset # Removes all installed dependencies and build artifacts
yarn start # Set up, install and run in dev mode
yarn dev # Set up, install and run in dev mode
```

## Editing the Code
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
"node": ">=22.20.0"
},
"scripts": {
"dev": "yarn build && yarn start --watch",
"build": "run install-and-build",
"start": "node ./scripts/run.mjs",
"prepare": "husky",
"postinstall": "run install:packages && run install:meteor",
"install:meteor": "yarn restart:meteor && cd meteor && meteor --version && meteor npm install -g yarn && node ../scripts/fix-windows-yarn.js && yarn install",
"install:packages": "cd packages && yarn install",
"start": "yarn install && run install-and-build && run dev",
"install-and-build": "node ./scripts/install-and-build.mjs",
"dev": "node ./scripts/run.mjs",
"restart:meteor": "node ./scripts/meteor-force-restart.js",
"build:packages": "cd packages && run build",
"test:packages": "cd packages && run test",
Expand Down
19 changes: 10 additions & 9 deletions scripts/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,24 @@ Options:
--db-list List all available database directories and show which is active

Examples:
yarn start # Install, build, then run in dev mode
yarn dev # Run in normal dev mode (requires prior build)
yarn dev --db-list # List all available databases
yarn dev --db=testing # Use a separate database for testing
yarn dev --db=demo # Switch to demo database
yarn dev --ui-only # Only watch UI, skip backend packages
yarn dev --inspect-meteor # Debug Meteor with inspector
yarn dev # Install, build, then run start with --watch
yarn start # Run in normal dev mode (requires prior build)
yarn start --watch # Run in dev mode with file watching
yarn start --db-list # List all available databases
yarn start --db=testing # Use a separate database for testing
yarn start --db=demo # Switch to demo database
yarn start --inspect-meteor # Debug Meteor with inspector
yarn start --db=demo # Install, build, and run with demo database
`);
process.exit(0);
}

// Parse --db=name option
const dbArg = args.find(arg => arg.startsWith('--db='));
const dbName = dbArg ? dbArg.split('=')[1] : null;
const dbArg = args.find((arg) => arg.startsWith("--db="));
const dbName = dbArg ? dbArg.split("=")[1] : null;

const config = {
watchMode: args.indexOf("--watch") >= 0 || false,
uiOnly: args.indexOf("--ui-only") >= 0 || false,
inspectMeteor: args.indexOf("--inspect-meteor") >= 0 || false,
verbose: args.indexOf("--verbose") >= 0 || false,
Expand Down
69 changes: 41 additions & 28 deletions scripts/run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function joinCommand(...parts) {
function watchPackages() {
return [
{
command: 'yarn watch --preserveWatchOutput',
command: "yarn watch --preserveWatchOutput",
cwd: "packages",
name: "TSC",
prefixColor: "red",
Expand All @@ -33,21 +33,21 @@ function watchWorker() {
function watchMeteor() {
const settingsFileExists = fs.existsSync("meteor-settings.json");
if (settingsFileExists) {
console.log('Found meteor-settings.json')
console.log("Found meteor-settings.json");
} else {
console.log('No meteor-settings.json')
console.log("No meteor-settings.json");
}

// If a ROOT_URL is defined, meteor will serve under that. We should use the same for vite, to get the correct proxying
const rootUrl = process.env.ROOT_URL ? new URL(process.env.ROOT_URL) : null
const rootUrl = process.env.ROOT_URL ? new URL(process.env.ROOT_URL) : null;

return [
{
command: joinCommand(
'yarn debug',
"yarn debug",
config.inspectMeteor ? " --inspect" : "",
config.verbose ? " --verbose" : "",
settingsFileExists ? " --settings ../meteor-settings.json" : ""
settingsFileExists ? " --settings ../meteor-settings.json" : "",
),
cwd: "meteor",
name: "METEOR",
Expand All @@ -59,7 +59,8 @@ function watchMeteor() {
name: "VITE",
prefixColor: "yellow",
env: {
SOFIE_BASE_PATH: rootUrl && rootUrl.pathname.length > 1 ? rootUrl.pathname : '',
SOFIE_BASE_PATH:
rootUrl && rootUrl.pathname.length > 1 ? rootUrl.pathname : "",
},
},
];
Expand All @@ -73,11 +74,11 @@ function hr() {
}

function listDatabases() {
const meteorLocalDir = path.join('meteor', '.meteor', 'local');
const dbLink = path.join(meteorLocalDir, 'db');
const meteorLocalDir = path.join("meteor", ".meteor", "local");
const dbLink = path.join(meteorLocalDir, "db");

if (!fs.existsSync(meteorLocalDir)) {
console.log('No databases found (meteor/.meteor/local does not exist yet)');
console.log("No databases found (meteor/.meteor/local does not exist yet)");
return;
}

Expand All @@ -92,35 +93,39 @@ function listDatabases() {
currentDb = match[1];
}
} else {
currentDb = '(unnamed - real directory)';
currentDb = "(unnamed - real directory)";
}
}

// List all db.* directories
const files = fs.readdirSync(meteorLocalDir);
const dbDirs = files
.filter(file => file.startsWith('db.') && fs.lstatSync(path.join(meteorLocalDir, file)).isDirectory())
.map(file => file.substring(3));

console.log('\nAvailable databases:');
.filter(
(file) =>
file.startsWith("db.") &&
fs.lstatSync(path.join(meteorLocalDir, file)).isDirectory(),
)
.map((file) => file.substring(3));

console.log("\nAvailable databases:");
if (dbDirs.length === 0) {
console.log(' (none found)');
console.log(" (none found)");
} else {
dbDirs.sort().forEach(db => {
const marker = db === currentDb ? ' ← current' : '';
dbDirs.sort().forEach((db) => {
const marker = db === currentDb ? " ← current" : "";
console.log(` ${db}${marker}`);
});
}

if (currentDb && !dbDirs.includes(currentDb)) {
console.log(`\nCurrent: ${currentDb}`);
}
console.log('');
console.log("");
}

function switchDatabase(dbName) {
const meteorLocalDir = path.join('meteor', '.meteor', 'local');
const dbLink = path.join(meteorLocalDir, 'db');
const meteorLocalDir = path.join("meteor", ".meteor", "local");
const dbLink = path.join(meteorLocalDir, "db");
const dbTarget = path.join(meteorLocalDir, `db.${dbName}`);

// Check if we're already using this database
Expand Down Expand Up @@ -148,21 +153,29 @@ function switchDatabase(dbName) {
fs.unlinkSync(dbLink);
} else {
// It's a real directory - back it up with timestamp
const defaultDb = path.join(meteorLocalDir, 'db.default');
const defaultDb = path.join(meteorLocalDir, "db.default");
if (!fs.existsSync(defaultDb)) {
console.log(`Backing up existing database to: default`);
fs.renameSync(dbLink, defaultDb);
} else {
// Default already exists, create timestamped backup instead of deleting
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19);
const timestamp = new Date()
.toISOString()
.replace(/[:.]/g, "-")
.substring(0, 19);
let backupName = path.join(meteorLocalDir, `db.backup.${timestamp}`);
// Ensure unique backup name
let suffix = 0;
while (fs.existsSync(backupName)) {
suffix++;
backupName = path.join(meteorLocalDir, `db.backup.${timestamp}.${suffix}`);
backupName = path.join(
meteorLocalDir,
`db.backup.${timestamp}.${suffix}`,
);
}
console.log(`Backing up existing database to: ${path.basename(backupName)}`);
console.log(
`Backing up existing database to: ${path.basename(backupName)}`,
);
fs.renameSync(dbLink, backupName);
}
}
Expand Down Expand Up @@ -193,15 +206,15 @@ try {
console.log(hr());
await concurrently(
[
...(config.uiOnly ? [] : watchPackages()),
...(config.uiOnly ? [] : watchWorker()),
...(config.watchMode ? watchPackages() : []),
...(config.watchMode ? watchWorker() : []),
...watchMeteor(),
],
{
prefix: "name",
killOthers: ["failure", "success"],
restartTries: 0,
}
},
).result;
} catch (e) {
console.error(e.message);
Expand Down
Loading