omegga 1.16.1 → 1.17.1
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/CHANGELOG.md +14 -0
- package/dist/brickadia/server.js +2 -2
- package/dist/brickadia/server.js.map +1 -1
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/plugin/git.js +155 -0
- package/dist/cli/plugin/git.js.map +1 -0
- package/dist/cli/plugin.js +36 -120
- package/dist/cli/plugin.js.map +1 -1
- package/dist/cli/terminal.js +9 -0
- package/dist/cli/terminal.js.map +1 -1
- package/dist/config/reader.js.map +1 -1
- package/dist/main.js +15 -4
- package/dist/main.js.map +1 -1
- package/dist/omegga/auth.js.map +1 -1
- package/dist/omegga/commandInjector.js.map +1 -1
- package/dist/omegga/logWrangler.js.map +1 -1
- package/dist/omegga/matchers/auth.js +1 -1
- package/dist/omegga/matchers/auth.js.map +1 -1
- package/dist/omegga/matchers/index.js +2 -2
- package/dist/omegga/matchers/index.js.map +1 -1
- package/dist/omegga/matchers/interact.js.map +1 -1
- package/dist/omegga/matchers/join.js +1 -1
- package/dist/omegga/matchers/join.js.map +1 -1
- package/dist/omegga/player.js +2 -2
- package/dist/omegga/player.js.map +1 -1
- package/dist/omegga/plugin/interface.js +1 -0
- package/dist/omegga/plugin/interface.js.map +1 -1
- package/dist/omegga/plugin/plugin_jsonrpc_stdio.js +4 -1
- package/dist/omegga/plugin/plugin_jsonrpc_stdio.js.map +1 -1
- package/dist/omegga/server.js.map +1 -1
- package/dist/omegga/wrapper.js.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/webserver/backend/database.js.map +1 -1
- package/dist/webserver/backend/index.js.map +1 -1
- package/dist/webserver/backend/metrics.js.map +1 -1
- package/dist/webserver/backend/roleHierarchy.js.map +1 -1
- package/dist/webserver/backend/router/mfa.js.map +1 -1
- package/dist/webserver/backend/router/role.js.map +1 -1
- package/dist/webserver/backend/util.js.map +1 -1
- package/dist/worker.js +1 -6
- package/dist/worker.js.map +1 -1
- package/docs/api/plugin.md +3 -3
- package/docs/api/types.md +1 -1
- package/docs/env.md +1 -0
- package/docs/install/linux.md +4 -1
- package/docs/plugins/installing.md +7 -1
- package/docs/running.md +6 -0
- package/eslint.config.mjs +16 -5
- package/package.json +3 -2
- package/public/app.html +4 -4
- package/public/assets/{app-4YRWLxdq.js → app-Mh--ChqF.js} +2 -2
- package/public/assets/{app-4YRWLxdq.js.map → app-Mh--ChqF.js.map} +1 -1
- package/public/assets/{auth-nn5rDnJ9.js → auth-DQtYicf-.js} +2 -2
- package/public/assets/{auth-nn5rDnJ9.js.map → auth-DQtYicf-.js.map} +1 -1
- package/public/assets/index-B0LowBt1.js +2 -0
- package/public/assets/index-B0LowBt1.js.map +1 -0
- package/public/assets/{index-X0t22aZ9.js → index-BTSbuqTr.js} +3 -3
- package/public/assets/{index-X0t22aZ9.js.map → index-BTSbuqTr.js.map} +1 -1
- package/public/assets/shared/{@components-L4cuUE3Y.js → @components-DBMXoJMT.js} +2 -2
- package/public/assets/shared/@components-DBMXoJMT.js.map +1 -0
- package/public/auth.html +3 -3
- package/templates/safe-ts/omegga.d.ts +11 -5
- package/public/assets/index-B2DCW1zi.js +0 -2
- package/public/assets/index-B2DCW1zi.js.map +0 -1
- package/public/assets/shared/@components-L4cuUE3Y.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## Latest
|
|
4
4
|
|
|
5
|
+
## 1.17.1 - 2026-09-03
|
|
6
|
+
|
|
7
|
+
- Add `omegga download`, which installs or updates the Brickadia server and exits, for provisioning a server without starting it
|
|
8
|
+
|
|
9
|
+
## 1.17.0 - 2026-09-03
|
|
10
|
+
|
|
11
|
+
Plugins install and update without git installed.
|
|
12
|
+
|
|
13
|
+
- Plugin installs and updates fall back to a bundled git implementation when the `git` binary is missing, so they work on hosts and container images without it. `OMEGGA_GIT` set to `bin` or `js` picks one explicitly
|
|
14
|
+
- Add `/plugins update [...plugin names]` to the console, alongside the existing `/plugins install`
|
|
15
|
+
- `omegga install` no longer exits when git is missing, and `omegga init-plugin` starts a repo whether or not it is
|
|
16
|
+
- `omegga check` no longer skips a plugin directory that is not a git repo
|
|
17
|
+
- A failed plugin update rolls back to the commit it started from, rather than leaving an `omegga-upgrade-backup` branch behind
|
|
18
|
+
|
|
5
19
|
## 1.16.1 - 2026-09-02
|
|
6
20
|
|
|
7
21
|
- Fix stopping the server or a JSON-RPC plugin crashing with `spawn kill ENOENT` on container images that ship no `kill` binary
|
package/dist/brickadia/server.js
CHANGED
|
@@ -336,9 +336,9 @@ class BrickadiaServer extends EventEmitter {
|
|
|
336
336
|
}
|
|
337
337
|
}
|
|
338
338
|
}
|
|
339
|
-
exitListener(
|
|
339
|
+
exitListener(code, signal) {
|
|
340
340
|
logger.default.verbose("Exit listener fired");
|
|
341
|
-
this.emit("closed",
|
|
341
|
+
this.emit("closed", code, signal);
|
|
342
342
|
this.cleanup();
|
|
343
343
|
}
|
|
344
344
|
lineListener(line) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sources":["../../src/brickadia/server.ts"],"sourcesContent":["/*\n Brickadia Server Wrapper\n Manages IO with the game server\n*/\n\nimport Logger from '@/logger';\nimport {\n ACTIVE_WORLD_FILE,\n CONFIG_SAVED_DIR,\n GAME_BIN_PATH,\n getOverrideGameBinary,\n getSteamGameDir,\n getSteamInstallDir,\n} from '@/softconfig';\nimport { getGlobalToken } from '@cli/auth';\nimport { type IConfig } from '@config/types';\nimport { checkWsl } from '@util/wsl';\nimport 'colors';\nimport { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport EventEmitter from 'node:events';\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { env } from 'node:process';\nimport readline from 'readline';\nimport stripAnsi from 'strip-ansi';\n\n// list of errors that can be solved by yelling at the user\nconst knownErrors: {\n name: string;\n solution?: string;\n match: RegExp;\n message?: string;\n}[] = [\n {\n name: 'MISSING_LIBGL',\n solution: 'apt-get install libgl1-mesa-glx libglib2.0-0',\n match:\n /error while loading shared libraries: libGL\\.so\\.1: cannot open shared object file/,\n },\n {\n name: 'MISSING_GLIB',\n solution: 'apt-get install libgl1-mesa-glx libglib2.0-0',\n match:\n /error while loading shared libraries: libgthread-2\\.0\\.so\\.0: cannot open shared object file/,\n },\n];\n\n/** Start a brickadia server */\nexport default class BrickadiaServer extends EventEmitter {\n #child: ChildProcessWithoutNullStreams | null = null;\n #errInterface: readline.Interface | null = null;\n #outInterface: readline.Interface | null = null;\n\n config: IConfig;\n path: string;\n\n constructor(dataPath: string, config: IConfig) {\n super();\n\n this.config = config;\n // use the data path if it's absolute, otherwise build an absolute path\n this.path =\n path.isAbsolute(dataPath) || dataPath.startsWith('/')\n ? dataPath\n : path.join(process.cwd(), dataPath);\n\n this.lineListener = this.lineListener.bind(this);\n this.errorListener = this.errorListener.bind(this);\n this.exitListener = this.exitListener.bind(this);\n }\n\n getActiveWorldFile(): string {\n return path.join(this.path, ACTIVE_WORLD_FILE);\n }\n\n /** A world specified by the active world file */\n getActiveWorld(): string | null {\n const activeWorldFile = this.getActiveWorldFile();\n if (existsSync(activeWorldFile)) {\n try {\n return readFileSync(activeWorldFile, 'utf8').trim();\n } catch (err) {\n Logger.errorp(\n 'Failed to read active world file',\n activeWorldFile.yellow,\n err,\n );\n return null;\n }\n }\n return null;\n }\n\n /** Set the world to use next startup */\n setActiveWorld(world: string | null): boolean {\n const activeWorldFile = this.getActiveWorldFile();\n if (!world || world === null) {\n if (existsSync(activeWorldFile)) {\n Logger.verbose('Removing active world file', activeWorldFile.yellow);\n unlinkSync(activeWorldFile);\n }\n return true;\n }\n\n if (!this.worldExists(world)) {\n Logger.verbose(\n 'Cannot set active world to',\n world.yellow,\n 'as it does not exist',\n );\n return false;\n }\n\n Logger.verbose('Setting active world to', world.yellow);\n try {\n writeFileSync(activeWorldFile, world, 'utf8');\n return true;\n } catch (err) {\n Logger.errorp(\n 'Failed to write active world file',\n activeWorldFile.yellow,\n err,\n );\n return false;\n }\n }\n\n /** A world specified by the config */\n getConfigWorld(): string | null {\n return this.config?.server?.world ?? null;\n }\n\n /** A world specified by the BRICKADIA_WORLD env variable */\n getEnvWorld(): string | null {\n return env.BRICKADIA_WORLD || null;\n }\n\n /** Check if a world exists */\n worldExists(world: string): boolean {\n const savedDir = this.config?.server?.savedDir ?? CONFIG_SAVED_DIR;\n const worldPath = path.join(this.path, savedDir, 'Worlds', world + '.brdb');\n return existsSync(worldPath);\n }\n\n /** Get the world that will be used next startup */\n getNextWorld(): { source: string; file: string } | null {\n const candidates = [\n { source: 'file', file: this.getActiveWorld() },\n { source: 'config', file: this.getConfigWorld() },\n { source: 'env', file: this.getEnvWorld() },\n ];\n\n return (\n candidates.find(\n (c): c is { source: string; file: string } =>\n !!c.file && this.worldExists(c.file),\n ) ?? null\n );\n }\n\n /**\n * Resolve the path to the game server binary for steam/override installs.\n * Returns null for launcher-managed installs, where the binary lives in a\n * branch directory the launcher owns and isn't known statically here.\n */\n getGameBinaryPath(): string | null {\n const overrideBinary = getOverrideGameBinary();\n if (overrideBinary) return overrideBinary;\n\n // a configured branch means the launcher manages the install, not steam\n const isSteam = !this.config.server.branch;\n if (!isSteam) return null;\n\n const steamBeta = this.config.server.steambeta ?? 'main';\n return path.join(\n getSteamInstallDir(), // steam install directory\n steamBeta, // steam beta branch (or main)\n getSteamGameDir(), // Brickadia\n GAME_BIN_PATH, // path to binary\n );\n }\n\n /**\n * Resolve the directory the game server binary lives in. Null whenever\n * {@link getGameBinaryPath} can't resolve a binary.\n */\n getGameBinaryDir(): string | null {\n const binary = this.getGameBinaryPath();\n return binary && path.dirname(binary);\n }\n\n // start the server child process\n start() {\n const {\n email,\n password,\n token: confToken,\n }: { email?: string; password?: string; token?: string } = this.config\n .credentials || {};\n\n const token = confToken || process.env.BRICKADIA_TOKEN || getGlobalToken();\n\n if (token) {\n Logger.verbose('Starting server with hosting token');\n } else {\n Logger.verbose(\n 'Starting server',\n (!email && !password ? 'without' : 'with').yellow,\n 'credentials',\n );\n }\n\n const isSteam = !this.config.server.branch;\n const steamBeta = this.config.server.steambeta ?? 'main';\n const overrideBinary = getOverrideGameBinary();\n const steamBinary = path.join(\n getSteamInstallDir(), // steam install directory\n steamBeta, // steam beta branch (or main)\n getSteamGameDir(), // Brickadia\n GAME_BIN_PATH, // path to binary\n );\n\n let gameBinary = this.getGameBinaryPath() ?? steamBinary;\n\n if (overrideBinary) {\n if (!existsSync(overrideBinary)) {\n Logger.error(\n 'Override binary',\n overrideBinary.yellow,\n 'does not exist!',\n );\n throw new Error(`Override binary ${overrideBinary} does not exist!`);\n }\n\n Logger.verbose(\n 'Using override binary',\n overrideBinary.yellow,\n 'instead of',\n steamBinary.yellow,\n );\n gameBinary = overrideBinary;\n } else if (isSteam) {\n Logger.verbose('Using steam binary', steamBeta.yellow);\n } else {\n Logger.verbose(\n 'Running',\n (this.config.server.__LOCAL\n ? path.join(__dirname, '../../tools/brickadia.sh')\n : 'brickadia launcher'\n ).yellow,\n );\n if (typeof this.config.server.branch === 'string')\n Logger.verbose('Using branch', this.config.server.branch.yellow);\n }\n\n // handle local launcher support\n const launchArgs = isSteam\n ? [gameBinary]\n : [\n this.config.server.__LOCAL\n ? path.join(__dirname, '../../tools/brickadia.sh')\n : 'brickadia',\n this.config.server.branch && `--branch=${this.config.server.branch}`,\n '--server',\n '--',\n ];\n\n const world = this.getNextWorld();\n if (world) {\n Logger.verbose(\n 'Using world',\n world.file.yellow,\n 'from',\n world.source.yellow,\n );\n } else if (this.config.server.map) {\n Logger.verbose('Using map', this.config.server.map.yellow, 'from config');\n }\n\n const params = [\n '--output=L',\n '--',\n ...launchArgs,\n !world &&\n this.config.server.map &&\n `-Environment=\"${this.config.server.map}\"`,\n world && `-World=\"${world.file}\"`,\n '-NotInstalled',\n '-log',\n // remote file access is on unless explicitly disabled in the config\n this.config.server.remoteFiles === false ? '-NoRemoteFileAccess' : null,\n checkWsl() === 1 ? '-OneThread' : null,\n this.path ? `-UserDir=\"${this.path}\"` : null,\n token ? `-Token=\"${token}\"` : null, // remove token argument if not provided\n !token && email ? `-User=\"${email}\"` : null, // remove email argument if not provided or token is provided\n !token && password ? `-Password=\"${password}\"` : null, // remove password argument if not provided or token is provided\n `-port=\"${this.config.server.port}\"`,\n this.config.server.launchArgs,\n ]\n .filter(arg => typeof arg === 'string')\n .filter(arg => arg.length > 0); // remove unused/empty arguments\n Logger.verbose(\n 'Params for spawn',\n params\n .join(' ')\n .replace(/-User=\".*?\"/, '-User=\"<hidden>\"')\n .replace(/-Password=\".*?\"/, '-Password=\"<hidden>\"')\n .replace(/-Token=\".*?\"/, '-Token=\"<hidden>\"')\n .replace(/-Cookie=\".*?\"/, '-Cookie=\"<hidden>\"')\n .replace(/-Cookie=\\S+/, '-Cookie=<hidden>'),\n );\n\n // Either unbuffer or stdbuf must be used because brickadia's output is buffered\n // this means that multiple lines can be bundled together if the output buffer is not full\n // unfortunately without stdbuf or unbuffer, the output would not happen immediately\n const child = spawn('stdbuf', params);\n this.#child = child;\n\n Logger.verbose('Spawn process', child.pid ?? 'failed'.red);\n\n child.stdin.setDefaultEncoding('utf8');\n this.#outInterface = readline.createInterface({\n input: child.stdout,\n terminal: false,\n });\n this.#errInterface = readline.createInterface({\n input: child.stderr,\n terminal: false,\n });\n this.attachListeners();\n Logger.verbose('Attached listeners');\n }\n\n // write a string to the child process\n write(line: string) {\n if (line.length >= 512) {\n // show a warning\n Logger.warn(\n 'WARNING'.yellow,\n 'The following line was called and is',\n 'longer than allowed limit'.red,\n );\n Logger.warn(line.replace(/\\n$/, ''));\n // throw a fake error to get the line number\n try {\n throw new Error('Console Line Too Long');\n } catch (err) {\n Logger.warn(err);\n }\n return;\n }\n if (this.#child) {\n Logger.verbose('WRITE'.green, line.replace(/\\n$/, ''));\n this.#child.stdin.write(line);\n }\n }\n\n // write a line to the child process\n writeln(line: string) {\n this.write(line + '\\n');\n }\n\n /**\n * pid of the running game process, or null when it isn't running.\n *\n * The child is spawned through `stdbuf`, which execs the game in place, so\n * this is the game's own pid and /proc entry.\n */\n get pid(): number | null {\n return this.#child?.pid ?? null;\n }\n\n // forcibly kills the server\n stop() {\n if (!this.#child) {\n Logger.verbose('Cannot stop server as no subprocess exists');\n return;\n }\n\n Logger.verbose('Forcibly stopping server');\n // kill the process\n this.#child.kill('SIGINT');\n\n // ...kill it again just to make sure it's dead. this signals the pid\n // directly rather than shelling out: slim container images have no\n // `kill` binary, only the shell builtin, and spawn cannot use that.\n this.#child.kill('SIGKILL');\n }\n\n // detaches listeners\n cleanup() {\n if (!this.#child) return;\n\n Logger.verbose('Cleaning up brickadia server');\n\n // detach listener\n this.detachListeners();\n\n this.#child = null;\n this.#outInterface = null;\n this.#errInterface = null;\n }\n\n // attaches proxy event listeners\n attachListeners() {\n this.#outInterface?.on('line', this.lineListener);\n this.#errInterface?.on('line', this.errorListener);\n this.#child?.on('exit', this.exitListener);\n this.#child?.on('close', () => {});\n }\n\n // removes previously attached proxy event listeners\n detachListeners() {\n this.#outInterface?.off('line', this.lineListener);\n this.#errInterface?.off('line', this.errorListener);\n this.#child?.off('exit', this.exitListener);\n this.#child?.removeAllListeners('close');\n }\n\n // -- listeners for basic events (line, err, exit)\n errorListener(line: string) {\n Logger.verbose('ERROR'.red, line);\n this.emit('err', line);\n for (const { match, solution, name, message } of knownErrors) {\n if (line.match(match)) {\n Logger.error(\n `Encountered ${name.red}. ${\n solution ? 'Known fix:\\n ' + solution : message || 'Unknown error.'\n }`,\n );\n }\n }\n }\n\n exitListener(...args: any[]) {\n Logger.verbose('Exit listener fired');\n this.emit('closed', ...args);\n this.cleanup();\n }\n\n lineListener(line: string) {\n this.emit('line', stripAnsi(line));\n }\n}\n"],"names":["ACTIVE_WORLD_FILE","existsSync","readFileSync","Logger","unlinkSync","writeFileSync","env","CONFIG_SAVED_DIR","getOverrideGameBinary","getSteamInstallDir","getSteamGameDir","GAME_BIN_PATH","getGlobalToken","checkWsl","spawn"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.js","sources":["../../src/brickadia/server.ts"],"sourcesContent":["/*\n Brickadia Server Wrapper\n Manages IO with the game server\n*/\n\nimport Logger from '@/logger';\nimport {\n ACTIVE_WORLD_FILE,\n CONFIG_SAVED_DIR,\n GAME_BIN_PATH,\n getOverrideGameBinary,\n getSteamGameDir,\n getSteamInstallDir,\n} from '@/softconfig';\nimport { getGlobalToken } from '@cli/auth';\nimport { type IConfig } from '@config/types';\nimport { checkWsl } from '@util/wsl';\nimport 'colors';\nimport { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport EventEmitter from 'node:events';\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { env } from 'node:process';\nimport readline from 'readline';\nimport stripAnsi from 'strip-ansi';\n\n// list of errors that can be solved by yelling at the user\nconst knownErrors: {\n name: string;\n solution?: string;\n match: RegExp;\n message?: string;\n}[] = [\n {\n name: 'MISSING_LIBGL',\n solution: 'apt-get install libgl1-mesa-glx libglib2.0-0',\n match:\n /error while loading shared libraries: libGL\\.so\\.1: cannot open shared object file/,\n },\n {\n name: 'MISSING_GLIB',\n solution: 'apt-get install libgl1-mesa-glx libglib2.0-0',\n match:\n /error while loading shared libraries: libgthread-2\\.0\\.so\\.0: cannot open shared object file/,\n },\n];\n\n/** Start a brickadia server */\nexport default class BrickadiaServer extends EventEmitter {\n #child: ChildProcessWithoutNullStreams | null = null;\n #errInterface: readline.Interface | null = null;\n #outInterface: readline.Interface | null = null;\n\n config: IConfig;\n path: string;\n\n constructor(dataPath: string, config: IConfig) {\n super();\n\n this.config = config;\n // use the data path if it's absolute, otherwise build an absolute path\n this.path =\n path.isAbsolute(dataPath) || dataPath.startsWith('/')\n ? dataPath\n : path.join(process.cwd(), dataPath);\n\n this.lineListener = this.lineListener.bind(this);\n this.errorListener = this.errorListener.bind(this);\n this.exitListener = this.exitListener.bind(this);\n }\n\n getActiveWorldFile(): string {\n return path.join(this.path, ACTIVE_WORLD_FILE);\n }\n\n /** A world specified by the active world file */\n getActiveWorld(): string | null {\n const activeWorldFile = this.getActiveWorldFile();\n if (existsSync(activeWorldFile)) {\n try {\n return readFileSync(activeWorldFile, 'utf8').trim();\n } catch (err) {\n Logger.errorp(\n 'Failed to read active world file',\n activeWorldFile.yellow,\n err,\n );\n return null;\n }\n }\n return null;\n }\n\n /** Set the world to use next startup */\n setActiveWorld(world: string | null): boolean {\n const activeWorldFile = this.getActiveWorldFile();\n if (!world || world === null) {\n if (existsSync(activeWorldFile)) {\n Logger.verbose('Removing active world file', activeWorldFile.yellow);\n unlinkSync(activeWorldFile);\n }\n return true;\n }\n\n if (!this.worldExists(world)) {\n Logger.verbose(\n 'Cannot set active world to',\n world.yellow,\n 'as it does not exist',\n );\n return false;\n }\n\n Logger.verbose('Setting active world to', world.yellow);\n try {\n writeFileSync(activeWorldFile, world, 'utf8');\n return true;\n } catch (err) {\n Logger.errorp(\n 'Failed to write active world file',\n activeWorldFile.yellow,\n err,\n );\n return false;\n }\n }\n\n /** A world specified by the config */\n getConfigWorld(): string | null {\n return this.config?.server?.world ?? null;\n }\n\n /** A world specified by the BRICKADIA_WORLD env variable */\n getEnvWorld(): string | null {\n return env.BRICKADIA_WORLD || null;\n }\n\n /** Check if a world exists */\n worldExists(world: string): boolean {\n const savedDir = this.config?.server?.savedDir ?? CONFIG_SAVED_DIR;\n const worldPath = path.join(this.path, savedDir, 'Worlds', world + '.brdb');\n return existsSync(worldPath);\n }\n\n /** Get the world that will be used next startup */\n getNextWorld(): { source: string; file: string } | null {\n const candidates = [\n { source: 'file', file: this.getActiveWorld() },\n { source: 'config', file: this.getConfigWorld() },\n { source: 'env', file: this.getEnvWorld() },\n ];\n\n return (\n candidates.find(\n (c): c is { source: string; file: string } =>\n !!c.file && this.worldExists(c.file),\n ) ?? null\n );\n }\n\n /**\n * Resolve the path to the game server binary for steam/override installs.\n * Returns null for launcher-managed installs, where the binary lives in a\n * branch directory the launcher owns and isn't known statically here.\n */\n getGameBinaryPath(): string | null {\n const overrideBinary = getOverrideGameBinary();\n if (overrideBinary) return overrideBinary;\n\n // a configured branch means the launcher manages the install, not steam\n const isSteam = !this.config.server.branch;\n if (!isSteam) return null;\n\n const steamBeta = this.config.server.steambeta ?? 'main';\n return path.join(\n getSteamInstallDir(), // steam install directory\n steamBeta, // steam beta branch (or main)\n getSteamGameDir(), // Brickadia\n GAME_BIN_PATH, // path to binary\n );\n }\n\n /**\n * Resolve the directory the game server binary lives in. Null whenever\n * {@link getGameBinaryPath} can't resolve a binary.\n */\n getGameBinaryDir(): string | null {\n const binary = this.getGameBinaryPath();\n return binary && path.dirname(binary);\n }\n\n // start the server child process\n start() {\n const {\n email,\n password,\n token: confToken,\n }: { email?: string; password?: string; token?: string } = this.config\n .credentials || {};\n\n const token = confToken || process.env.BRICKADIA_TOKEN || getGlobalToken();\n\n if (token) {\n Logger.verbose('Starting server with hosting token');\n } else {\n Logger.verbose(\n 'Starting server',\n (!email && !password ? 'without' : 'with').yellow,\n 'credentials',\n );\n }\n\n const isSteam = !this.config.server.branch;\n const steamBeta = this.config.server.steambeta ?? 'main';\n const overrideBinary = getOverrideGameBinary();\n const steamBinary = path.join(\n getSteamInstallDir(), // steam install directory\n steamBeta, // steam beta branch (or main)\n getSteamGameDir(), // Brickadia\n GAME_BIN_PATH, // path to binary\n );\n\n let gameBinary = this.getGameBinaryPath() ?? steamBinary;\n\n if (overrideBinary) {\n if (!existsSync(overrideBinary)) {\n Logger.error(\n 'Override binary',\n overrideBinary.yellow,\n 'does not exist!',\n );\n throw new Error(`Override binary ${overrideBinary} does not exist!`);\n }\n\n Logger.verbose(\n 'Using override binary',\n overrideBinary.yellow,\n 'instead of',\n steamBinary.yellow,\n );\n gameBinary = overrideBinary;\n } else if (isSteam) {\n Logger.verbose('Using steam binary', steamBeta.yellow);\n } else {\n Logger.verbose(\n 'Running',\n (this.config.server.__LOCAL\n ? path.join(__dirname, '../../tools/brickadia.sh')\n : 'brickadia launcher'\n ).yellow,\n );\n if (typeof this.config.server.branch === 'string')\n Logger.verbose('Using branch', this.config.server.branch.yellow);\n }\n\n // handle local launcher support\n const launchArgs = isSteam\n ? [gameBinary]\n : [\n this.config.server.__LOCAL\n ? path.join(__dirname, '../../tools/brickadia.sh')\n : 'brickadia',\n this.config.server.branch && `--branch=${this.config.server.branch}`,\n '--server',\n '--',\n ];\n\n const world = this.getNextWorld();\n if (world) {\n Logger.verbose(\n 'Using world',\n world.file.yellow,\n 'from',\n world.source.yellow,\n );\n } else if (this.config.server.map) {\n Logger.verbose('Using map', this.config.server.map.yellow, 'from config');\n }\n\n const params = [\n '--output=L',\n '--',\n ...launchArgs,\n !world &&\n this.config.server.map &&\n `-Environment=\"${this.config.server.map}\"`,\n world && `-World=\"${world.file}\"`,\n '-NotInstalled',\n '-log',\n // remote file access is on unless explicitly disabled in the config\n this.config.server.remoteFiles === false ? '-NoRemoteFileAccess' : null,\n checkWsl() === 1 ? '-OneThread' : null,\n this.path ? `-UserDir=\"${this.path}\"` : null,\n token ? `-Token=\"${token}\"` : null, // remove token argument if not provided\n !token && email ? `-User=\"${email}\"` : null, // remove email argument if not provided or token is provided\n !token && password ? `-Password=\"${password}\"` : null, // remove password argument if not provided or token is provided\n `-port=\"${this.config.server.port}\"`,\n this.config.server.launchArgs,\n ]\n .filter(arg => typeof arg === 'string')\n .filter(arg => arg.length > 0); // remove unused/empty arguments\n Logger.verbose(\n 'Params for spawn',\n params\n .join(' ')\n .replace(/-User=\".*?\"/, '-User=\"<hidden>\"')\n .replace(/-Password=\".*?\"/, '-Password=\"<hidden>\"')\n .replace(/-Token=\".*?\"/, '-Token=\"<hidden>\"')\n .replace(/-Cookie=\".*?\"/, '-Cookie=\"<hidden>\"')\n .replace(/-Cookie=\\S+/, '-Cookie=<hidden>'),\n );\n\n // Either unbuffer or stdbuf must be used because brickadia's output is buffered\n // this means that multiple lines can be bundled together if the output buffer is not full\n // unfortunately without stdbuf or unbuffer, the output would not happen immediately\n const child = spawn('stdbuf', params);\n this.#child = child;\n\n Logger.verbose('Spawn process', child.pid ?? 'failed'.red);\n\n child.stdin.setDefaultEncoding('utf8');\n this.#outInterface = readline.createInterface({\n input: child.stdout,\n terminal: false,\n });\n this.#errInterface = readline.createInterface({\n input: child.stderr,\n terminal: false,\n });\n this.attachListeners();\n Logger.verbose('Attached listeners');\n }\n\n // write a string to the child process\n write(line: string) {\n if (line.length >= 512) {\n // show a warning\n Logger.warn(\n 'WARNING'.yellow,\n 'The following line was called and is',\n 'longer than allowed limit'.red,\n );\n Logger.warn(line.replace(/\\n$/, ''));\n // throw a fake error to get the line number\n try {\n throw new Error('Console Line Too Long');\n } catch (err) {\n Logger.warn(err);\n }\n return;\n }\n if (this.#child) {\n Logger.verbose('WRITE'.green, line.replace(/\\n$/, ''));\n this.#child.stdin.write(line);\n }\n }\n\n // write a line to the child process\n writeln(line: string) {\n this.write(line + '\\n');\n }\n\n /**\n * pid of the running game process, or null when it isn't running.\n *\n * The child is spawned through `stdbuf`, which execs the game in place, so\n * this is the game's own pid and /proc entry.\n */\n get pid(): number | null {\n return this.#child?.pid ?? null;\n }\n\n // forcibly kills the server\n stop() {\n if (!this.#child) {\n Logger.verbose('Cannot stop server as no subprocess exists');\n return;\n }\n\n Logger.verbose('Forcibly stopping server');\n // kill the process\n this.#child.kill('SIGINT');\n\n // ...kill it again just to make sure it's dead. this signals the pid\n // directly rather than shelling out: slim container images have no\n // `kill` binary, only the shell builtin, and spawn cannot use that.\n this.#child.kill('SIGKILL');\n }\n\n // detaches listeners\n cleanup() {\n if (!this.#child) return;\n\n Logger.verbose('Cleaning up brickadia server');\n\n // detach listener\n this.detachListeners();\n\n this.#child = null;\n this.#outInterface = null;\n this.#errInterface = null;\n }\n\n // attaches proxy event listeners\n attachListeners() {\n this.#outInterface?.on('line', this.lineListener);\n this.#errInterface?.on('line', this.errorListener);\n this.#child?.on('exit', this.exitListener);\n this.#child?.on('close', () => {});\n }\n\n // removes previously attached proxy event listeners\n detachListeners() {\n this.#outInterface?.off('line', this.lineListener);\n this.#errInterface?.off('line', this.errorListener);\n this.#child?.off('exit', this.exitListener);\n this.#child?.removeAllListeners('close');\n }\n\n // -- listeners for basic events (line, err, exit)\n errorListener(line: string) {\n Logger.verbose('ERROR'.red, line);\n this.emit('err', line);\n for (const { match, solution, name, message } of knownErrors) {\n if (line.match(match)) {\n Logger.error(\n `Encountered ${name.red}. ${\n solution ? 'Known fix:\\n ' + solution : message || 'Unknown error.'\n }`,\n );\n }\n }\n }\n\n exitListener(code: number | null, signal: NodeJS.Signals | null) {\n Logger.verbose('Exit listener fired');\n this.emit('closed', code, signal);\n this.cleanup();\n }\n\n lineListener(line: string) {\n this.emit('line', stripAnsi(line));\n }\n}\n"],"names":["ACTIVE_WORLD_FILE","existsSync","readFileSync","Logger","unlinkSync","writeFileSync","env","CONFIG_SAVED_DIR","getOverrideGameBinary","getSteamInstallDir","getSteamGameDir","GAME_BIN_PATH","getGlobalToken","checkWsl","spawn"],"mappings":"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"}
|
package/dist/cli/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sources":["../../src/cli/config.ts"],"sourcesContent":["import { store } from '@config';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nconst err = (...args:
|
|
1
|
+
{"version":3,"file":"config.js","sources":["../../src/cli/config.ts"],"sourcesContent":["import { store } from '@config';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nconst err = (...args: unknown[]) => console.error('!>'.red, ...args);\nconst log = (...args: unknown[]) => console.log('>>'.green, ...args);\n\ninterface IConfigField {\n desc: string;\n example: string;\n default: string;\n get(): string;\n set(value: string): void;\n}\n\nconst fields: Record<string, IConfigField> = {\n default: {\n desc: 'default omegga project directory when running omegga in a non-configured folder',\n example: 'omegga config default $(pwd)',\n default: '.',\n get() {\n let val = store.get('defaultOmegga') || '.';\n if (val === '.') val += ' [current working dir]'.grey;\n return val;\n },\n set(val: string) {\n if (!val) {\n val = '.';\n }\n\n // check if the path is valid\n if (val !== '.' && !path.isAbsolute(val)) {\n err('setting must be an absolute path or \".\"');\n process.exit(1);\n return;\n }\n if (!fs.existsSync(val)) {\n err('given path does not exist');\n process.exit(1);\n return;\n }\n if (!fs.statSync(val).isDirectory) {\n err('given path is not for a directory');\n process.exit(1);\n return;\n }\n\n // update the setting\n store.set('defaultOmegga', val);\n log('Set', 'default'.green, 'to', val.green);\n },\n },\n};\n\nexport default (\n field: keyof typeof fields | 'list',\n value: string,\n _opts = {},\n) => {\n // list command lists all fields, current setting, and description\n if (field === 'list') {\n log('Configurable fields:');\n const maxLen = Math.max(...Object.keys(fields).map(f => f.length));\n for (const key in fields) {\n const field = fields[key];\n console.log(\n '\\n ' + key.yellow.underline,\n '-'.padStart(maxLen - key.length + 1),\n field.desc,\n );\n console.log(' current'.brightGreen + ':', field.get());\n console.log(' default:', field.default);\n console.log(' example'.brightBlue + ':', field.example);\n }\n console.log();\n } else if (fields[field]) {\n // a valid field was entered - run the set command\n fields[field].set(value);\n } else {\n // no valid field was entered\n err(\n 'invalid field, run',\n 'omegga config list'.yellow.underline,\n 'for a list of available fields',\n );\n process.exit(1);\n }\n};\n"],"names":["store","field"],"mappings":";;;;;AAIA,MAAM,MAAM,IAAI,SAAoB,QAAQ,MAAM,KAAK,KAAK,GAAG,IAAI;AACnE,MAAM,MAAM,IAAI,SAAoB,QAAQ,IAAI,KAAK,OAAO,GAAG,IAAI;AAUnE,MAAM,SAAuC;AAAA,EAC3C,SAAS;AAAA,IACP,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AACJ,UAAI,MAAMA,MAAAA,MAAM,IAAI,eAAe,KAAK;AACxC,UAAI,QAAQ,IAAK,QAAO,yBAAyB;AACjD,aAAO;AAAA,IACT;AAAA,IACA,IAAI,KAAa;AACf,UAAI,CAAC,KAAK;AACR,cAAM;AAAA,MACR;AAGA,UAAI,QAAQ,OAAO,CAAC,KAAK,WAAW,GAAG,GAAG;AACxC,YAAI,yCAAyC;AAC7C,gBAAQ,KAAK,CAAC;AACd;AAAA,MACF;AACA,UAAI,CAAC,GAAG,WAAW,GAAG,GAAG;AACvB,YAAI,2BAA2B;AAC/B,gBAAQ,KAAK,CAAC;AACd;AAAA,MACF;AACA,UAAI,CAAC,GAAG,SAAS,GAAG,EAAE,aAAa;AACjC,YAAI,mCAAmC;AACvC,gBAAQ,KAAK,CAAC;AACd;AAAA,MACF;AAGAA,kBAAM,IAAI,iBAAiB,GAAG;AAC9B,UAAI,OAAO,UAAU,OAAO,MAAM,IAAI,KAAK;AAAA,IAC7C;AAAA,EAAA;AAEJ;AAEA,MAAA,UAAe,CACb,OACA,OACA,QAAQ,OACL;AAEH,MAAI,UAAU,QAAQ;AACpB,QAAI,sBAAsB;AAC1B,UAAM,SAAS,KAAK,IAAI,GAAG,OAAO,KAAK,MAAM,EAAE,IAAI,CAAA,MAAK,EAAE,MAAM,CAAC;AACjE,eAAW,OAAO,QAAQ;AACxB,YAAMC,SAAQ,OAAO,GAAG;AACxB,cAAQ;AAAA,QACN,SAAS,IAAI,OAAO;AAAA,QACpB,IAAI,SAAS,SAAS,IAAI,SAAS,CAAC;AAAA,QACpCA,OAAM;AAAA,MAAA;AAER,cAAQ,IAAI,cAAc,cAAc,KAAKA,OAAM,KAAK;AACxD,cAAQ,IAAI,gBAAgBA,OAAM,OAAO;AACzC,cAAQ,IAAI,cAAc,aAAa,KAAKA,OAAM,OAAO;AAAA,IAC3D;AACA,YAAQ,IAAA;AAAA,EACV,WAAW,OAAO,KAAK,GAAG;AAExB,WAAO,KAAK,EAAE,IAAI,KAAK;AAAA,EACzB,OAAO;AAEL;AAAA,MACE;AAAA,MACA,qBAAqB,OAAO;AAAA,MAC5B;AAAA,IAAA;AAEF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const logger = require("../../logger.js");
|
|
4
|
+
require("colors");
|
|
5
|
+
const hasbin = require("hasbin");
|
|
6
|
+
const isoGit = require("isomorphic-git");
|
|
7
|
+
const http = require("isomorphic-git/http/node");
|
|
8
|
+
const fs = require("node:fs");
|
|
9
|
+
const path = require("node:path");
|
|
10
|
+
const simpleGit = require("simple-git");
|
|
11
|
+
const binGit = {
|
|
12
|
+
backend: "bin",
|
|
13
|
+
async isRepo(dir) {
|
|
14
|
+
if (!fs.existsSync(dir)) return false;
|
|
15
|
+
try {
|
|
16
|
+
return await simpleGit(dir).checkIsRepo();
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
async init(dir) {
|
|
22
|
+
await simpleGit(dir).init();
|
|
23
|
+
},
|
|
24
|
+
async clone(url, dir) {
|
|
25
|
+
await simpleGit(dir).clone(url, dir);
|
|
26
|
+
},
|
|
27
|
+
async isDirty(dir) {
|
|
28
|
+
const status = await simpleGit(dir).status();
|
|
29
|
+
return status.files.some((f) => f.working_dir !== "?");
|
|
30
|
+
},
|
|
31
|
+
async currentBranch(dir) {
|
|
32
|
+
const current = (await simpleGit(dir).branch()).current;
|
|
33
|
+
if (current) return current;
|
|
34
|
+
try {
|
|
35
|
+
const ref = await simpleGit(dir).raw(["symbolic-ref", "--short", "HEAD"]);
|
|
36
|
+
return ref.trim() || null;
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
async headSha(dir) {
|
|
42
|
+
return (await simpleGit(dir).revparse(["HEAD"])).trim();
|
|
43
|
+
},
|
|
44
|
+
async fetch(dir) {
|
|
45
|
+
await simpleGit(dir).fetch();
|
|
46
|
+
},
|
|
47
|
+
async compareUpstream(dir) {
|
|
48
|
+
const status = await simpleGit(dir).status();
|
|
49
|
+
if (!status.tracking) return null;
|
|
50
|
+
return { ahead: status.ahead > 0, behind: status.behind > 0 };
|
|
51
|
+
},
|
|
52
|
+
async fastForward(dir) {
|
|
53
|
+
await simpleGit(dir).pull(["--ff-only"]);
|
|
54
|
+
},
|
|
55
|
+
async resetTo(dir, sha) {
|
|
56
|
+
await simpleGit(dir).reset(simpleGit.ResetMode.HARD, [sha]);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const jsGit = {
|
|
60
|
+
backend: "js",
|
|
61
|
+
async isRepo(dir) {
|
|
62
|
+
return fs.existsSync(path.join(dir, ".git"));
|
|
63
|
+
},
|
|
64
|
+
async init(dir) {
|
|
65
|
+
await isoGit.init({ fs, dir });
|
|
66
|
+
},
|
|
67
|
+
async clone(url, dir) {
|
|
68
|
+
await isoGit.clone({ fs, http, dir, url });
|
|
69
|
+
},
|
|
70
|
+
async isDirty(dir) {
|
|
71
|
+
const rows = await isoGit.statusMatrix({ fs, dir });
|
|
72
|
+
return rows.some(
|
|
73
|
+
([, head, workdir, stage]) => head !== 0 && (head !== workdir || head !== stage)
|
|
74
|
+
);
|
|
75
|
+
},
|
|
76
|
+
async currentBranch(dir) {
|
|
77
|
+
return await isoGit.currentBranch({ fs, dir, fullname: false }) ?? null;
|
|
78
|
+
},
|
|
79
|
+
async headSha(dir) {
|
|
80
|
+
return await isoGit.resolveRef({ fs, dir, ref: "HEAD" });
|
|
81
|
+
},
|
|
82
|
+
async fetch(dir) {
|
|
83
|
+
await isoGit.fetch({ fs, http, dir, singleBranch: true });
|
|
84
|
+
},
|
|
85
|
+
async compareUpstream(dir) {
|
|
86
|
+
const branch = await this.currentBranch(dir);
|
|
87
|
+
if (!branch) return null;
|
|
88
|
+
const remotes = await isoGit.listRemotes({ fs, dir });
|
|
89
|
+
if (remotes.length === 0) return null;
|
|
90
|
+
let upstream;
|
|
91
|
+
try {
|
|
92
|
+
upstream = await isoGit.resolveRef({
|
|
93
|
+
fs,
|
|
94
|
+
dir,
|
|
95
|
+
ref: `refs/remotes/origin/${branch}`
|
|
96
|
+
});
|
|
97
|
+
} catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const head = await this.headSha(dir);
|
|
101
|
+
if (head === upstream) return { ahead: false, behind: false };
|
|
102
|
+
return {
|
|
103
|
+
behind: await isoGit.isDescendent({
|
|
104
|
+
fs,
|
|
105
|
+
dir,
|
|
106
|
+
oid: upstream,
|
|
107
|
+
ancestor: head
|
|
108
|
+
}),
|
|
109
|
+
ahead: await isoGit.isDescendent({
|
|
110
|
+
fs,
|
|
111
|
+
dir,
|
|
112
|
+
oid: head,
|
|
113
|
+
ancestor: upstream
|
|
114
|
+
})
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
async fastForward(dir) {
|
|
118
|
+
await isoGit.fastForward({ fs, http, dir, singleBranch: false });
|
|
119
|
+
},
|
|
120
|
+
async resetTo(dir, sha) {
|
|
121
|
+
const branch = await this.currentBranch(dir);
|
|
122
|
+
if (branch) {
|
|
123
|
+
await isoGit.writeRef({
|
|
124
|
+
fs,
|
|
125
|
+
dir,
|
|
126
|
+
ref: `refs/heads/${branch}`,
|
|
127
|
+
value: sha,
|
|
128
|
+
force: true
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
await isoGit.checkout({ fs, dir, ref: branch ?? sha, force: true });
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
const hasGitBinary = () => hasbin.sync("git");
|
|
135
|
+
let cached;
|
|
136
|
+
function getPluginGit() {
|
|
137
|
+
if (cached) return cached;
|
|
138
|
+
const override = process.env.OMEGGA_GIT?.trim().toLowerCase();
|
|
139
|
+
if (override === "bin" || override === "js") {
|
|
140
|
+
cached = override === "bin" ? binGit : jsGit;
|
|
141
|
+
logger.default.verbose("Using", override.yellow, "git backend from OMEGGA_GIT");
|
|
142
|
+
return cached;
|
|
143
|
+
}
|
|
144
|
+
if (override) {
|
|
145
|
+
logger.default.warnp(
|
|
146
|
+
`Ignoring ${"OMEGGA_GIT".yellow}: ${override.yellow} is not ${"bin".yellow} or ${"js".yellow}`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
cached = hasGitBinary() ? binGit : jsGit;
|
|
150
|
+
logger.default.verbose("Using", cached.backend.yellow, "git backend");
|
|
151
|
+
return cached;
|
|
152
|
+
}
|
|
153
|
+
exports.getPluginGit = getPluginGit;
|
|
154
|
+
exports.hasGitBinary = hasGitBinary;
|
|
155
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sources":["../../../src/cli/plugin/git.ts"],"sourcesContent":["import Logger from '@/logger';\nimport 'colors';\nimport hasbin from 'hasbin';\nimport isoGit from 'isomorphic-git';\nimport http from 'isomorphic-git/http/node';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport simpleGit, { ResetMode } from 'simple-git';\n\n/**\n * Which implementation of git the plugin commands run on.\n *\n * `bin` shells out to the git binary, `js` uses isomorphic-git, which speaks\n * the same on-disk format: a plugin cloned by one can be updated by the other.\n */\nexport type GitBackend = 'bin' | 'js';\n\n/** the git operations plugin install, update, and check need */\nexport interface PluginGit {\n backend: GitBackend;\n isRepo(dir: string): Promise<boolean>;\n /** start a repo in an existing directory, as `git init` does */\n init(dir: string): Promise<void>;\n clone(url: string, dir: string): Promise<void>;\n /**\n * Whether a tracked file has been modified, staged, or deleted. Untracked\n * files do not count: plugins routinely write next to their own source.\n */\n isDirty(dir: string): Promise<boolean>;\n currentBranch(dir: string): Promise<string | null>;\n headSha(dir: string): Promise<string>;\n /** bring remote refs up to date without touching the working tree */\n fetch(dir: string): Promise<void>;\n /**\n * How the checked out branch stands against its upstream, or null when it\n * has no upstream to stand against. Call `fetch` first.\n */\n compareUpstream(\n dir: string,\n ): Promise<{ ahead: boolean; behind: boolean } | null>;\n /** advance the checked out branch to its upstream, refusing to merge */\n fastForward(dir: string): Promise<void>;\n /** move the branch back to a commit and match the working tree to it */\n resetTo(dir: string, sha: string): Promise<void>;\n}\n\nconst binGit: PluginGit = {\n backend: 'bin',\n\n async isRepo(dir) {\n if (!fs.existsSync(dir)) return false;\n try {\n return await simpleGit(dir).checkIsRepo();\n } catch {\n return false;\n }\n },\n\n async init(dir) {\n await simpleGit(dir).init();\n },\n\n async clone(url, dir) {\n await simpleGit(dir).clone(url, dir);\n },\n\n async isDirty(dir) {\n const status = await simpleGit(dir).status();\n return status.files.some(f => f.working_dir !== '?');\n },\n\n async currentBranch(dir) {\n const current = (await simpleGit(dir).branch()).current;\n if (current) return current;\n // a repo with no commits yet reports no branch, though HEAD still points\n // at the one the first commit would land on\n try {\n const ref = await simpleGit(dir).raw(['symbolic-ref', '--short', 'HEAD']);\n return ref.trim() || null;\n } catch {\n return null;\n }\n },\n\n async headSha(dir) {\n return (await simpleGit(dir).revparse(['HEAD'])).trim();\n },\n\n async fetch(dir) {\n await simpleGit(dir).fetch();\n },\n\n async compareUpstream(dir) {\n const status = await simpleGit(dir).status();\n if (!status.tracking) return null;\n return { ahead: status.ahead > 0, behind: status.behind > 0 };\n },\n\n async fastForward(dir) {\n await simpleGit(dir).pull(['--ff-only']);\n },\n\n async resetTo(dir, sha) {\n await simpleGit(dir).reset(ResetMode.HARD, [sha]);\n },\n};\n\nconst jsGit: PluginGit = {\n backend: 'js',\n\n async isRepo(dir) {\n return fs.existsSync(path.join(dir, '.git'));\n },\n\n async init(dir) {\n await isoGit.init({ fs, dir });\n },\n\n async clone(url, dir) {\n await isoGit.clone({ fs, http, dir, url });\n },\n\n async isDirty(dir) {\n // rows are [filepath, head, workdir, stage]; head 0 means untracked\n const rows = await isoGit.statusMatrix({ fs, dir });\n return rows.some(\n ([, head, workdir, stage]) =>\n head !== 0 && (head !== workdir || head !== stage),\n );\n },\n\n async currentBranch(dir) {\n return (await isoGit.currentBranch({ fs, dir, fullname: false })) ?? null;\n },\n\n async headSha(dir) {\n return await isoGit.resolveRef({ fs, dir, ref: 'HEAD' });\n },\n\n async fetch(dir) {\n await isoGit.fetch({ fs, http, dir, singleBranch: true });\n },\n\n async compareUpstream(dir) {\n const branch = await this.currentBranch(dir);\n if (!branch) return null;\n\n const remotes = await isoGit.listRemotes({ fs, dir });\n if (remotes.length === 0) return null;\n\n let upstream: string;\n try {\n upstream = await isoGit.resolveRef({\n fs,\n dir,\n ref: `refs/remotes/origin/${branch}`,\n });\n } catch {\n // the branch has never been fetched, so there is nothing to compare to\n return null;\n }\n\n const head = await this.headSha(dir);\n if (head === upstream) return { ahead: false, behind: false };\n\n return {\n behind: await isoGit.isDescendent({\n fs,\n dir,\n oid: upstream,\n ancestor: head,\n }),\n ahead: await isoGit.isDescendent({\n fs,\n dir,\n oid: head,\n ancestor: upstream,\n }),\n };\n },\n\n async fastForward(dir) {\n await isoGit.fastForward({ fs, http, dir, singleBranch: false });\n },\n\n async resetTo(dir, sha) {\n const branch = await this.currentBranch(dir);\n // isomorphic-git has no `reset --hard`, so the branch is moved by hand\n // before the working tree is checked out against it\n if (branch) {\n await isoGit.writeRef({\n fs,\n dir,\n ref: `refs/heads/${branch}`,\n value: sha,\n force: true,\n });\n }\n await isoGit.checkout({ fs, dir, ref: branch ?? sha, force: true });\n },\n};\n\n/** whether the git binary is on PATH */\nexport const hasGitBinary = () => hasbin.sync('git');\n\nlet cached: PluginGit | undefined;\n\n/**\n * The git binary is preferred when it is installed: it is faster and it is what\n * a plugin author's own checkout was made with. isomorphic-git covers hosts\n * without it, which is most container images.\n *\n * `OMEGGA_GIT` forces one or the other, so the implementation that is not\n * native to a machine can still be exercised there.\n */\nexport function getPluginGit(): PluginGit {\n if (cached) return cached;\n\n const override = process.env.OMEGGA_GIT?.trim().toLowerCase();\n if (override === 'bin' || override === 'js') {\n cached = override === 'bin' ? binGit : jsGit;\n Logger.verbose('Using', override.yellow, 'git backend from OMEGGA_GIT');\n return cached;\n }\n if (override) {\n Logger.warnp(\n `Ignoring ${'OMEGGA_GIT'.yellow}: ${override.yellow} is not ${'bin'.yellow} or ${'js'.yellow}`,\n );\n }\n\n cached = hasGitBinary() ? binGit : jsGit;\n Logger.verbose('Using', cached.backend.yellow, 'git backend');\n return cached;\n}\n\n/** only for tests, which need a fresh choice per case */\nexport function resetPluginGit() {\n cached = undefined;\n}\n"],"names":["ResetMode","Logger"],"mappings":"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"}
|
package/dist/cli/plugin.js
CHANGED
|
@@ -4,14 +4,13 @@ const logger = require("../logger.js");
|
|
|
4
4
|
const softconfig = require("../softconfig.js");
|
|
5
5
|
const index = require("../config/index.js");
|
|
6
6
|
const plugin = require("../omegga/plugin.js");
|
|
7
|
+
const git = require("./plugin/git.js");
|
|
7
8
|
const node_child_process = require("node:child_process");
|
|
8
9
|
require("colors");
|
|
9
10
|
const fs = require("node:fs");
|
|
10
|
-
const hasbin = require("hasbin");
|
|
11
11
|
const path = require("node:path");
|
|
12
12
|
const prompts = require("prompts");
|
|
13
13
|
const semver = require("semver");
|
|
14
|
-
const simpleGit = require("simple-git");
|
|
15
14
|
const node_util = require("node:util");
|
|
16
15
|
const version = require("../version.js");
|
|
17
16
|
const exec = node_util.promisify(node_child_process.exec);
|
|
@@ -206,9 +205,9 @@ async function install(plugins, _options) {
|
|
|
206
205
|
}
|
|
207
206
|
}
|
|
208
207
|
try {
|
|
208
|
+
const git$1 = git.getPluginGit();
|
|
209
209
|
plgLog(plugin2, "Cloning...");
|
|
210
|
-
|
|
211
|
-
await git.clone(plugin2.url, pluginPath);
|
|
210
|
+
await git$1.clone(plugin2.url, pluginPath);
|
|
212
211
|
} catch (e) {
|
|
213
212
|
plgErr(plugin2, "Error cloning plugin", e);
|
|
214
213
|
}
|
|
@@ -240,13 +239,14 @@ async function update(pluginsNames, _options) {
|
|
|
240
239
|
"omegga init".yellow,
|
|
241
240
|
"to setup one."
|
|
242
241
|
);
|
|
242
|
+
const git$1 = git.getPluginGit();
|
|
243
243
|
const pluginFolder = path.join(omeggaPath, softconfig.default.PLUGIN_PATH);
|
|
244
244
|
if (pluginsNames.length === 0) {
|
|
245
245
|
pluginsNames = fs.readdirSync(pluginFolder);
|
|
246
246
|
}
|
|
247
247
|
const plugins = pluginsNames.map((dir) => path.join(pluginFolder, dir)).filter(
|
|
248
248
|
(dir) => fs.existsSync(dir) && fs.lstatSync(dir).isDirectory() && fs.existsSync(path.join(dir, ".git"))
|
|
249
|
-
).map((dir) => ({ name: path.basename(dir) }));
|
|
249
|
+
).map((dir) => ({ name: path.basename(dir), path: dir }));
|
|
250
250
|
if (plugins.length === 0) {
|
|
251
251
|
log("Found no plugins that can be updated");
|
|
252
252
|
return;
|
|
@@ -259,76 +259,33 @@ async function update(pluginsNames, _options) {
|
|
|
259
259
|
needsNL = false;
|
|
260
260
|
}
|
|
261
261
|
plgLog(plugin2, "Checking...");
|
|
262
|
-
const pluginPath = path.join(pluginFolder, plugin2.name);
|
|
263
|
-
plugin2.path = pluginPath;
|
|
264
|
-
const git = simpleGit(pluginPath);
|
|
265
|
-
plugin2.git = git;
|
|
266
|
-
if (!await git.checkIsRepo()) {
|
|
267
|
-
plgErr(plugin2, "No git repo detected");
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
let status;
|
|
271
|
-
try {
|
|
272
|
-
status = await git.status();
|
|
273
|
-
} catch (e) {
|
|
274
|
-
plgErr(plugin2, "Error getting status", e);
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
262
|
try {
|
|
278
|
-
if (
|
|
263
|
+
if (await git$1.isDirty(plugin2.path)) {
|
|
279
264
|
plgErr(plugin2, "Detected uncommitted changes - ignoring");
|
|
280
265
|
continue;
|
|
281
266
|
}
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
if (!MAIN_BRANCHES.includes(branches.current)) {
|
|
267
|
+
const branch = await git$1.currentBranch(plugin2.path);
|
|
268
|
+
if (!branch || !MAIN_BRANCHES.includes(branch)) {
|
|
285
269
|
plgErr(plugin2, "Not on a main branch - ignoring");
|
|
286
270
|
continue;
|
|
287
271
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
plgLog(plugin2, "Correcting upstream...");
|
|
293
|
-
try {
|
|
294
|
-
await git.branch({ "--set-upstream-to": `origin/${branch}` });
|
|
295
|
-
status = await git.status();
|
|
296
|
-
break;
|
|
297
|
-
} catch (e) {
|
|
298
|
-
plgErr(plugin2, "Error getting status/fixing upstream branch", e);
|
|
299
|
-
skip = true;
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
if (skip) continue;
|
|
305
|
-
}
|
|
306
|
-
if (!status.tracking) {
|
|
272
|
+
plgLog(plugin2, "Fetching...");
|
|
273
|
+
await git$1.fetch(plugin2.path);
|
|
274
|
+
const upstream = await git$1.compareUpstream(plugin2.path);
|
|
275
|
+
if (!upstream) {
|
|
307
276
|
plgErr(plugin2, "No upstream branch - ignoring");
|
|
308
277
|
continue;
|
|
309
278
|
}
|
|
310
|
-
if (
|
|
311
|
-
plgErr(plugin2, "No remotes - ignoring");
|
|
312
|
-
continue;
|
|
313
|
-
}
|
|
314
|
-
try {
|
|
315
|
-
plgLog(plugin2, "Fetching...");
|
|
316
|
-
await git.fetch();
|
|
317
|
-
status = await git.status();
|
|
318
|
-
} catch (e) {
|
|
319
|
-
plgErr(plugin2, "Error fetching remote code", e);
|
|
320
|
-
continue;
|
|
321
|
-
}
|
|
322
|
-
if (status.ahead > 0) {
|
|
279
|
+
if (upstream.ahead) {
|
|
323
280
|
plgErr(plugin2, "Detected plugin is ahead - ignoring");
|
|
324
281
|
continue;
|
|
325
282
|
}
|
|
326
|
-
if (
|
|
283
|
+
if (!upstream.behind) {
|
|
327
284
|
plgLog(plugin2, "Already up-to-date!".green);
|
|
328
285
|
continue;
|
|
329
286
|
}
|
|
330
287
|
plgLog(plugin2, "Update available");
|
|
331
|
-
pluginsToUpdate.push(
|
|
288
|
+
pluginsToUpdate.push(plugin2);
|
|
332
289
|
} catch (e) {
|
|
333
290
|
plgErr(plugin2, "Error", e);
|
|
334
291
|
}
|
|
@@ -340,27 +297,20 @@ async function update(pluginsNames, _options) {
|
|
|
340
297
|
log("Updating", (pluginsToUpdate.length + "").yellow, "plugins...");
|
|
341
298
|
let updates = 0;
|
|
342
299
|
for (const plugin2 of pluginsToUpdate) {
|
|
343
|
-
const { git } = plugin2;
|
|
344
300
|
if (needsNL) {
|
|
345
301
|
console.log();
|
|
346
302
|
needsNL = false;
|
|
347
303
|
}
|
|
348
|
-
|
|
304
|
+
let previous;
|
|
305
|
+
try {
|
|
306
|
+
previous = await git$1.headSha(plugin2.path);
|
|
307
|
+
} catch (e) {
|
|
308
|
+
plgErr(plugin2, "Error reading current commit", e);
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
349
311
|
try {
|
|
350
|
-
const branches = await git.branch();
|
|
351
|
-
const mainBranch = MAIN_BRANCHES.find((b) => branches.branches[b]) ?? MAIN_BRANCHES[0];
|
|
352
|
-
if (branches.branches["omegga-upgrade-backup"]) {
|
|
353
|
-
plgLog(plugin2, "Deleting leftover backup branch...");
|
|
354
|
-
await git.deleteLocalBranch("omegga-upgrade-backup", true);
|
|
355
|
-
}
|
|
356
|
-
await git.checkoutBranch("omegga-upgrade-backup", mainBranch);
|
|
357
|
-
await git.branch({ "--set-upstream-to": `origin/${mainBranch}` });
|
|
358
|
-
await git.checkout(mainBranch);
|
|
359
312
|
plgLog(plugin2, "Pulling update...");
|
|
360
|
-
await git.
|
|
361
|
-
if ((await git.status()).behind > 0) {
|
|
362
|
-
throw "- still behind?";
|
|
363
|
-
}
|
|
313
|
+
await git$1.fastForward(plugin2.path);
|
|
364
314
|
plgLog(plugin2, "Checking plugin versions...");
|
|
365
315
|
if (!checkPlugin(omeggaPath, plugin2)) {
|
|
366
316
|
throw "Incompatible";
|
|
@@ -383,42 +333,13 @@ async function update(pluginsNames, _options) {
|
|
|
383
333
|
plgLog(plugin2, "Updated!".green);
|
|
384
334
|
updates++;
|
|
385
335
|
} catch (e) {
|
|
386
|
-
plgErr(
|
|
387
|
-
plugin2,
|
|
388
|
-
"Error updating - attempting to restore from backup branch",
|
|
389
|
-
e
|
|
390
|
-
);
|
|
336
|
+
plgErr(plugin2, "Error updating plugin", e);
|
|
391
337
|
try {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
"Not on expected branch - exiting before I break more things"
|
|
397
|
-
);
|
|
398
|
-
continue;
|
|
399
|
-
}
|
|
400
|
-
const mainBranch = MAIN_BRANCHES.find((b) => branches.branches[b]) ?? MAIN_BRANCHES[0];
|
|
401
|
-
plg(plugin2, "Resetting current branch");
|
|
402
|
-
await git.reset(simpleGit.ResetMode.HARD);
|
|
403
|
-
plgLog(plugin2, "Attempting to checkout backup branch");
|
|
404
|
-
await git.checkout("omegga-upgrade-backup");
|
|
405
|
-
plgLog(plugin2, `Replacing ${mainBranch} with backup`);
|
|
406
|
-
await git.deleteLocalBranch(mainBranch);
|
|
407
|
-
await git.checkoutBranch(mainBranch, "omegga-upgrade-backup");
|
|
408
|
-
plgLog(plugin2, "Restored to backup branch");
|
|
409
|
-
if ((await git.branch()).current !== mainBranch) {
|
|
410
|
-
plgErr(
|
|
411
|
-
plugin2,
|
|
412
|
-
`Failed to checkout newly created ${mainBranch} branch`
|
|
413
|
-
);
|
|
414
|
-
continue;
|
|
415
|
-
} else {
|
|
416
|
-
plgWarn(plugin2, "Restored from backup and plugin not updated...");
|
|
417
|
-
}
|
|
418
|
-
} catch {
|
|
419
|
-
plgErr(plugin2, "Error restoring from backup... Whelp...");
|
|
338
|
+
plgWarn(plugin2, "Rolling back to", previous.slice(0, 8).yellow);
|
|
339
|
+
await git$1.resetTo(plugin2.path, previous);
|
|
340
|
+
} catch (rollback) {
|
|
341
|
+
plgErr(plugin2, "Error rolling back", rollback);
|
|
420
342
|
}
|
|
421
|
-
continue;
|
|
422
343
|
}
|
|
423
344
|
}
|
|
424
345
|
log("Updated", (updates + "").yellow, "plugins!");
|
|
@@ -448,14 +369,7 @@ async function check(pluginNames, _options) {
|
|
|
448
369
|
"plugins for valid plugin files"
|
|
449
370
|
);
|
|
450
371
|
for (const plugin2 of plugins) {
|
|
451
|
-
|
|
452
|
-
plugin2.path = pluginPath;
|
|
453
|
-
const git = simpleGit(pluginPath);
|
|
454
|
-
plugin2.git = git;
|
|
455
|
-
if (!await git.checkIsRepo()) {
|
|
456
|
-
plgErr(plugin2, "No git repo detected");
|
|
457
|
-
continue;
|
|
458
|
-
}
|
|
372
|
+
plugin2.path = path.join(pluginFolder, plugin2.name);
|
|
459
373
|
checkPlugin(omeggaPath, plugin2);
|
|
460
374
|
}
|
|
461
375
|
console.log();
|
|
@@ -481,7 +395,7 @@ async function init() {
|
|
|
481
395
|
]
|
|
482
396
|
},
|
|
483
397
|
{
|
|
484
|
-
type: (prev) => prev
|
|
398
|
+
type: (prev) => prev === "safe" ? "confirm" : null,
|
|
485
399
|
name: "ts",
|
|
486
400
|
message: "Would you like to use " + "TypeScript".yellow + " in your plugin?",
|
|
487
401
|
initial: false
|
|
@@ -493,7 +407,7 @@ async function init() {
|
|
|
493
407
|
}
|
|
494
408
|
]);
|
|
495
409
|
const name = response.name;
|
|
496
|
-
const type = response.type
|
|
410
|
+
const type = response.type === "safe" && response.ts ? "safe-ts" : response.type;
|
|
497
411
|
if (!PLUGIN_TYPES.includes(type)) {
|
|
498
412
|
err("Invalid plugin type", type.red, "!");
|
|
499
413
|
process.exit(1);
|
|
@@ -540,9 +454,11 @@ async function init() {
|
|
|
540
454
|
const src = path.join(__dirname, `../../templates/${type}`);
|
|
541
455
|
verboseLog("Copying and rendering template...");
|
|
542
456
|
await copyAndRender(src, dest);
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
await
|
|
457
|
+
verboseLog("Starting a repo in the new plugin directory ...");
|
|
458
|
+
try {
|
|
459
|
+
await git.getPluginGit().init(dest);
|
|
460
|
+
} catch (e) {
|
|
461
|
+
log("Warning: could not start a git repo here.".yellow, e);
|
|
546
462
|
}
|
|
547
463
|
if (fs.existsSync(path.join(src, "package.json"))) {
|
|
548
464
|
verboseLog("Running", "npm i".yellow, "in the new plugin directory ...");
|