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
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,9 @@ You can download the latest Linux builds under the [Releases](https://github.com
- **.rpm:** The intended format for Fedora based distributions

## Known Issues About Container Runtimes
- Podman is **unsupported** for now
- Docker Desktop is **unsupported** for now
- Distros that emulate Docker through a Podman socket are **unsupported**
- Any rootless containerization solution is currently **unsupported**
- Rootless podman cannot do USB passthrough.

## Building WinBoat
- For building you need to have NodeJS and Go installed on your system
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type WinboatConfigObj = {
customApps: WinApp[]
experimentalFeatures: boolean
multiMonitor: number
containerRuntime: string
};

const defaultConfig: WinboatConfigObj = {
Expand All @@ -22,6 +23,7 @@ const defaultConfig: WinboatConfigObj = {
customApps: [],
experimentalFeatures: false,
multiMonitor: 0,
containerRuntime: "docker",
};

export class WinboatConfig {
Expand Down Expand Up @@ -101,4 +103,4 @@ export class WinboatConfig {
return { ...defaultConfig };
}
}
}
}
8 changes: 6 additions & 2 deletions src/renderer/lib/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const path: typeof import('path') = require('path');
const { promisify }: typeof import('util') = require('util');
const nodeFetch: typeof import('node-fetch').default = require('node-fetch');
const remote: typeof import('@electron/remote') = require('@electron/remote');
import { WinboatConfig } from './config';

const execAsync = promisify(exec);
const logger = createLogger(path.join(WINBOAT_DIR, 'install.log'));
Expand Down Expand Up @@ -83,12 +84,14 @@ export class InstallManager {
emitter: Emitter<InstallEvents>;
state: InstallState;
preinstallMsg: string;
wbConfig: WinboatConfig | null

constructor(conf: InstallConfiguration) {
this.conf = conf;
this.state = InstallStates.IDLE;
this.preinstallMsg = ""
this.emitter = createNanoEvents<InstallEvents>();
this.wbConfig = new WinboatConfig();
}

changeState(newState: InstallState) {
Expand Down Expand Up @@ -240,7 +243,7 @@ export class InstallManager {
// Start the container
try {
// execSync(`docker compose -f ${composeFilePath} up -d`, { stdio: 'inherit' });
const { stdout, stderr } = await execAsync(`docker compose -f ${composeFilePath} up -d`);
const { stdout, stderr } = await execAsync(`${this.wbConfig!.config.containerRuntime} compose -f ${composeFilePath} up -d`);
if (stderr) {
logger.error(stderr);
}
Expand Down Expand Up @@ -331,8 +334,9 @@ export class InstallManager {

export async function isInstalled() {
// Check if a docker container named WinBoat exists
const wbConfig: WinboatConfig | null = new WinboatConfig();
try {
const { stdout: res } = await execAsync('docker ps -a --filter "name=WinBoat" --format "{{.Names}}"');
const { stdout: res } = await execAsync(`${wbConfig!.config.containerRuntime} ps -a --filter "name=WinBoat" --format "{{.Names}}"`);
return res.includes('WinBoat');
} catch(e) {
logger.error("Failed to get WinBoat status, is Docker installed?");
Expand Down
15 changes: 10 additions & 5 deletions src/renderer/lib/specs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getFreeRDP } from '../utils/getFreeRDP';
const fs: typeof import('fs') = require('fs');
const os: typeof import('os') = require('os');
import { WinboatConfig } from './config';
const { exec }: typeof import('child_process') = require('child_process');
const { promisify }: typeof import('util') = require('util');
const execAsync = promisify(exec);
Expand All @@ -20,6 +21,7 @@ export const defaultSpecs: Specs = {
cpuCores: 0,
ramGB: 0,
kvmEnabled: false,
usingDocker: true,
dockerInstalled: false,
dockerComposeInstalled: false,
dockerIsRunning: false,
Expand All @@ -30,6 +32,9 @@ export const defaultSpecs: Specs = {
export async function getSpecs() {
const specs: Specs = { ...defaultSpecs };

let wbConfig: WinboatConfig | null = new WinboatConfig(); // Instantiate singleton class
specs.usingDocker = (wbConfig!.config.containerRuntime == "docker")

// Physical CPU cores check
try {
const res = (await execAsync('lscpu -p | egrep -v "^#" | sort -u -t, -k 2,4 | wc -l')).stdout;
Expand Down Expand Up @@ -59,15 +64,15 @@ export async function getSpecs() {

// Docker check
try {
const { stdout: dockerOutput } = await execAsync('docker --version');
let { stdout: dockerOutput } = await execAsync(`${wbConfig!.config.containerRuntime} --version`);
specs.dockerInstalled = !!dockerOutput;
} catch (e) {
console.error('Error checking for Docker installation:', e);
}

// Docker Compose plugin check with version validation
try {
const { stdout: dockerComposeOutput } = await execAsync('docker compose version');
const { stdout: dockerComposeOutput } = await execAsync(`${wbConfig!.config.containerRuntime} compose version`);
if (dockerComposeOutput) {
// Example output: "Docker Compose version v2.35.1"
// Example output 2: "Docker Compose version 2.36.2"
Expand All @@ -87,16 +92,16 @@ export async function getSpecs() {

// Docker is running check
try {
const { stdout: dockerOutput } = await execAsync('docker ps');
const { stdout: dockerOutput } = await execAsync(`${wbConfig!.config.containerRuntime} ps`);
specs.dockerIsRunning = !!dockerOutput;
} catch (e) {
console.error('Error checking if Docker is running:', e);
console.error('Error checking if Container Manager is running:', e);
}

// Docker user group check
try {
const userGroups = (await execAsync('id -Gn')).stdout;
specs.dockerIsInUserGroups = userGroups.split(/\s+/).includes('docker');
specs.dockerIsInUserGroups = (wbConfig!.config.containerRuntime != "docker" || userGroups.split(/\s+/).includes('docker'));
} catch (e) {
console.error('Error checking user groups for docker:', e);
}
Expand Down
22 changes: 11 additions & 11 deletions src/renderer/lib/winboat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,10 +374,10 @@ export class Winboat {

async getContainerStatus() {
try {
const { stdout: _containerStatus } = await execAsync(`docker inspect --format="{{.State.Status}}" WinBoat`);
const { stdout: _containerStatus } = await execAsync(`${this.#wbConfig!.config.containerRuntime} inspect --format="{{.State.Status}}" WinBoat`);
return _containerStatus.trim() as ContainerStatusValue;
} catch(e) {
console.error("Failed to get container status, most likely we are in the process of resetting");
console.error(`Failed to get container status, most likely we are in the process of resetting ${this.#wbConfig!.config.containerRuntime}`);
return ContainerStatus.Dead;
}
}
Expand Down Expand Up @@ -448,7 +448,7 @@ export class Winboat {
logger.info("Starting WinBoat container...");
this.containerActionLoading.value = true;
try {
const { stdout } = await execAsync("docker container start WinBoat");
const { stdout } = await execAsync(`${this.#wbConfig!.config.containerRuntime} container start WinBoat`);
logger.info(`Container response: ${stdout}`);
} catch(e) {
logger.error("There was an error performing the container action.");
Expand All @@ -463,7 +463,7 @@ export class Winboat {
logger.info("Stopping WinBoat container...");
this.containerActionLoading.value = true;
try {
const { stdout } = await execAsync("docker container stop WinBoat");
const { stdout } = await execAsync(`${this.#wbConfig!.config.containerRuntime} container stop WinBoat`);
logger.info(`Container response: ${stdout}`);
} catch(e) {
logger.error("There was an error performing the container action.");
Expand All @@ -478,7 +478,7 @@ export class Winboat {
logger.info("Pausing WinBoat container...");
this.containerActionLoading.value = true;
try {
const { stdout } = await execAsync("docker container pause WinBoat");
const { stdout } = await execAsync(`${this.#wbConfig!.config.containerRuntime} container pause WinBoat`);
logger.info(`Container response: ${stdout}`);
// TODO: The heartbeat check should set this, but it doesn't because normal fetch timeout doesn't exist
// Fix it once you change fetch to something else
Expand All @@ -496,7 +496,7 @@ export class Winboat {
logger.info("Unpausing WinBoat container...");
this.containerActionLoading.value = true;
try {
const { stdout } = await execAsync("docker container unpause WinBoat");
const { stdout } = await execAsync(`${this.#wbConfig!.config.containerRuntime} container unpause WinBoat`);
logger.info(`Container response: ${stdout}`);
} catch(e) {
logger.error("There was an error performing the container action.");
Expand All @@ -519,7 +519,7 @@ export class Winboat {
}

// 1. Compose down the current container
await execAsync(`docker compose -f ${composeFilePath} down`);
await execAsync(`${this.#wbConfig!.config.containerRuntime} compose -f ${composeFilePath} down`);

// 2. Create a backup directory if it doesn't exist
const backupDir = path.join(WINBOAT_DIR, 'backup');
Expand All @@ -539,7 +539,7 @@ export class Winboat {
logger.info(`Wrote new compose file to: ${composeFilePath}`);

// 5. Deploy the container with the new compose file
await execAsync(`docker compose -f ${composeFilePath} up -d`);
await execAsync(`${this.#wbConfig!.config.containerRuntime} compose -f ${composeFilePath} up -d`);

logger.info("Replace compose config completed, successfully deployed new container");

Expand All @@ -554,15 +554,15 @@ export class Winboat {
console.info("Stopped container");

// 2. Remove the container
await execAsync("docker rm WinBoat")
await execAsync(`${this.#wbConfig!.config.containerRuntime} rm WinBoat`)
console.info("Removed container")

// 3. Remove the container volume or folder
const compose = this.parseCompose();
const storage = compose.services.windows.volumes.find(vol => vol.includes('/storage'));
if (storage?.startsWith("data:")) {
// In this case we have a volume (legacy)
await execAsync("docker volume rm winboat_data");
await execAsync(`${this.#wbConfig!.config.containerRuntime} volume rm winboat_data`);
console.info("Removed volume");
} else {
const storageFolder = storage?.split(":").at(0) ?? null;
Expand Down Expand Up @@ -716,4 +716,4 @@ export class Winboat {
get hasQMPInterval() {
return this.#qmpInterval !== null;
}
}
}
2 changes: 1 addition & 1 deletion src/renderer/utils/getFreeRDP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ export async function getFreeRDP() {
} catch {}
}
return null;
}
}
6 changes: 3 additions & 3 deletions src/renderer/views/SetupUI.vue
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
<li class="flex items-center gap-2">
<span v-if="specs.dockerInstalled" class="text-green-500">✔</span>
<span v-else class="text-red-500">✘</span>
Docker installed
Container manager installed
<a href="https://docs.docker.com/engine/install/" @click="openAnchorLink" target="_blank" class="text-violet-400 hover:underline ml-1">How?</a>
</li>
<li class="flex items-center gap-2">
Expand All @@ -88,7 +88,7 @@
Docker Compose v2 installed
<a href="https://docs.docker.com/compose/install/#plugin-linux-only" @click="openAnchorLink" target="_blank" class="text-violet-400 hover:underline ml-1">How?</a>
</li>
<li class="flex items-center gap-2">
<li class="flex items-center gap-2" v-if="specs.usingDocker">
<span v-if="specs.dockerIsInUserGroups" class="text-green-500">✔</span>
<span v-else class="text-red-500">✘</span>
User added to the <span class="font-mono bg-neutral-700 rounded-md px-0.5">docker</span> group
Expand All @@ -97,7 +97,7 @@
</span>
<a href="https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user" @click="openAnchorLink" target="_blank" class="text-violet-400 hover:underline ml-1">How?</a>
</li>
<li class="flex items-center gap-2">
<li class="flex items-center gap-2" v-if="specs.usingDocker">
<span v-if="specs.dockerIsRunning" class="text-green-500">✔</span>
<span v-else class="text-red-500">✘</span>
Docker daemon is running
Expand Down