diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index f13dc6a..7da67a8 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -6,8 +6,8 @@ on:
workflow_dispatch:
jobs:
- build:
- name: Build/Test
+ CLI:
+ name: Build/Test CLI
runs-on: ubuntu-latest
steps:
@@ -31,20 +31,19 @@ jobs:
- name: Install Docker and Docker Compose
run: |
sudo apt-get update
- for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
+ for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove -y $pkg; done
sudo apt-get update
- sudo apt-get install ca-certificates curl
+ sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
- # Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
- $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
+ $(. /etc/os-release && echo \"$VERSION_CODENAME\") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
- sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
+ sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
- name: Set Up SSH Config for CI
run: |
@@ -63,19 +62,49 @@ jobs:
run: docker compose -f docker-compose.yml up -d
- name: Wait for Local Containers to Initialize
- run: sleep 10 # Ensure services are fully started
+ run: sleep 10
- name: Run Installation Script
- run: |
- python3 install.py
+ run: python3 install.py
- name: Verify Monitor Commands
run: |
- echo "🔄 Testing monitor --service"
+ echo "🔄 Testing monitor service"
monitor service || { echo "❌ monitor service failed"; exit 1; }
- echo "🔄 Testing monitor --state"
+ echo "🔄 Testing monitor state"
monitor state || { echo "❌ monitor state failed"; exit 1; }
- name: Stop and Clean Up Docker Containers
run: docker compose down
+
+ ElectronApp:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v3
+
+ - name: Set up Node
+ uses: actions/setup-node@v3
+ with:
+ node-version: '16'
+
+ - name: Install Dependencies
+ run: |
+ cd electron_app
+ npm install
+
+ - name: Fix Electron sandbox
+ run: |
+ cd electron_app
+ sudo chown root:root node_modules/electron/dist/chrome-sandbox
+ sudo chmod 4755 node_modules/electron/dist/chrome-sandbox
+
+ - name: Install xvfb
+ run: sudo apt-get update && sudo apt-get install -y xvfb
+
+ - name: Run Electron Tests
+ run: |
+ cd electron_app
+ xvfb-run -a npm test
diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml
index 95e3857..b80ae2e 100644
--- a/.github/workflows/CICD.yml
+++ b/.github/workflows/CICD.yml
@@ -90,7 +90,6 @@ jobs:
name: Create GitHub Release
needs: build
runs-on: ubuntu-latest
- # Release tylko przy pushu do main
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
steps:
diff --git a/electron_app/index.html b/electron_app/index.html
new file mode 100644
index 0000000..ab2dbcd
--- /dev/null
+++ b/electron_app/index.html
@@ -0,0 +1,64 @@
+
+
+
+
+ Electron Docker Monitor
+
+
+
+ Electron Docker Monitor
+
+
+ No data yet
+
+
+
+ Remote Hosts
+
+
+
+
+
+
+
diff --git a/electron_app/main.js b/electron_app/main.js
new file mode 100644
index 0000000..91127b3
--- /dev/null
+++ b/electron_app/main.js
@@ -0,0 +1,58 @@
+// Main process
+const { app, BrowserWindow, ipcMain } = require('electron');
+const path = require('path');
+const Docker = require('dockerode');
+const { getSSHHosts } = require('./sshUtils');
+
+let docker = new Docker();
+
+function createWindow() {
+ const win = new BrowserWindow({
+ width: 900,
+ height: 600,
+ webPreferences: {
+ contextIsolation: true,
+ preload: path.join(__dirname, 'preload.js')
+ }
+ });
+ win.loadFile('index.html');
+}
+
+app.whenReady().then(() => {
+ createWindow();
+ app.on('activate', () => {
+ if (BrowserWindow.getAllWindows().length === 0) {
+ createWindow();
+ }
+ });
+});
+
+app.on('window-all-closed', () => {
+ if (process.platform !== 'darwin') app.quit();
+});
+
+ipcMain.handle('start-poll', async () => {
+ return fetchContainers();
+});
+
+// SSH host list
+ipcMain.handle('get-ssh-hosts', async () => {
+ return getSSHHosts();
+});
+
+ipcMain.handle('connect-remote', async (event, hostAlias) => {
+ return `Connected to remote host: ${hostAlias}`;
+});
+
+async function fetchContainers() {
+ try {
+ const containers = await docker.listContainers({ all: true });
+ return containers.map((c) => ({
+ name: c.Names[0],
+ status: c.Status,
+ ports: c.Ports
+ }));
+ } catch (err) {
+ return [];
+ }
+}
diff --git a/electron_app/package.json b/electron_app/package.json
new file mode 100644
index 0000000..b411f94
--- /dev/null
+++ b/electron_app/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "monitor-electron",
+ "version": "1.0.0",
+ "main": "main.js",
+ "scripts": {
+ "start": "electron .",
+ "test": "mocha test/e2e.spec.js"
+ },
+ "dependencies": {
+ "electron": "^34.2.0",
+ "dockerode": "^4.0.4",
+ "ssh-config": "^5.0.3"
+ },
+ "devDependencies": {
+ "spectron": "^15.0.0",
+ "mocha": "^10.2.0",
+ "chai": "^4.3.7"
+ }
+}
diff --git a/electron_app/preload.js b/electron_app/preload.js
new file mode 100644
index 0000000..88c313a
--- /dev/null
+++ b/electron_app/preload.js
@@ -0,0 +1,14 @@
+// Expose API to renderer
+const { contextBridge, ipcRenderer } = require('electron');
+
+contextBridge.exposeInMainWorld('electronAPI', {
+ startPoll: async () => {
+ return await ipcRenderer.invoke('start-poll');
+ },
+ getSSHHosts: async () => {
+ return await ipcRenderer.invoke('get-ssh-hosts');
+ },
+ connectRemote: async (hostAlias) => {
+ return await ipcRenderer.invoke('connect-remote', hostAlias);
+ }
+});
diff --git a/electron_app/sshUtils.js b/electron_app/sshUtils.js
new file mode 100644
index 0000000..b354372
--- /dev/null
+++ b/electron_app/sshUtils.js
@@ -0,0 +1,19 @@
+const fs = require('fs');
+const path = require('path');
+const SSHConfig = require('ssh-config');
+
+function getSSHHosts() {
+ try {
+ const configPath = path.join(process.env.HOME || process.env.USERPROFILE, '.ssh', 'config');
+ const data = fs.readFileSync(configPath, 'utf8');
+ const parsed = SSHConfig.parse(data);
+ return parsed
+ .filter((item) => item.type === SSHConfig.DIRECTIVE && item.param === 'Host')
+ .map((item) => item.value)
+ .filter((host) => host !== '*');
+ } catch (err) {
+ return [];
+ }
+}
+
+module.exports = { getSSHHosts };
diff --git a/electron_app/test/e2e.spec.js b/electron_app/test/e2e.spec.js
new file mode 100644
index 0000000..c8720a2
--- /dev/null
+++ b/electron_app/test/e2e.spec.js
@@ -0,0 +1,46 @@
+// Spectron E2E test
+const { Application } = require('spectron');
+const assert = require('chai').assert;
+const path = require('path');
+
+describe('Electron App Tests', function() {
+ this.timeout(10000); // Increase if needed
+ let app;
+
+ before(async () => {
+ // Path to local electron bin
+ const electronPath = path.join(__dirname, '..', 'node_modules', '.bin', 'electron');
+ // App root
+ const appPath = path.join(__dirname, '..');
+
+ app = new Application({
+ path: electronPath,
+ args: [appPath]
+ });
+
+ await app.start();
+ });
+
+ after(async () => {
+ if (app && app.isRunning()) {
+ await app.stop();
+ }
+ });
+
+ it('shows the main window', async () => {
+ const count = await app.client.getWindowCount();
+ assert.equal(count, 1, 'Main window not found');
+ });
+
+ it('has correct title', async () => {
+ const title = await app.client.getTitle();
+ assert.equal(title, 'Electron Docker Monitor', 'Title mismatch');
+ });
+
+ it('renders UI elements', async () => {
+ // Example: check if a button or text is present
+ const button = await app.client.$('#btnStart');
+ const exists = await button.isExisting();
+ assert.isTrue(exists, 'btnStart not found');
+ });
+});