diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 4413187c3..4af91578e 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -11,8 +11,6 @@ on:
type: boolean
default: true
-env:
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
determine-version:
@@ -62,13 +60,13 @@ jobs:
- uses: actions/setup-node@v4
with:
- node-version: "20"
+ node-version: "24"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build with Turbo
- run: pnpm turbo build --filter=testing-view
+ run: pnpm turbo build --filter=testing-view --filter=competition-view
- uses: actions/upload-artifact@v4
with:
@@ -76,6 +74,12 @@ jobs:
path: frontend/testing-view/dist/**
retention-days: 1
+ - uses: actions/upload-artifact@v4
+ with:
+ name: competition-dist
+ path: frontend/competition-view/dist/**
+ retention-days: 1
+
build-backend:
name: Build Backend - ${{ matrix.os }}
needs: determine-version
@@ -156,13 +160,19 @@ jobs:
name: frontend-dist
path: electron-app/renderer/testing-view
+ - name: Download competition-view dist
+ uses: actions/download-artifact@v4
+ with:
+ name: competition-dist
+ path: electron-app/renderer/competition-view
+
- uses: pnpm/action-setup@v4
with:
version: 10.26.0
- uses: actions/setup-node@v4
with:
- node-version: "20"
+ node-version: "24"
- name: Update version in package.json
working-directory: electron-app
@@ -186,7 +196,7 @@ jobs:
run: |
find electron-app/dist -maxdepth 1 -type f \
\( -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" \
- -o -name "*.dmg" -o -name "*.zip" -o -name "*.yml" -o -name "*.blockmap" \) \
+ -o -name "*.rpm" -o -name "*.dmg" -o -name "*.zip" -o -name "*.yml" -o -name "*.blockmap" \) \
| xargs gh release upload v${{ needs.determine-version.outputs.version }} --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/electron-app/.gitignore b/electron-app/.gitignore
index 2f4d5e1f1..a931cbac6 100644
--- a/electron-app/.gitignore
+++ b/electron-app/.gitignore
@@ -10,6 +10,8 @@ renderer
!renderer/
renderer/testing-view/
renderer/flashing-view/
+renderer/competition-view/
+# include mode-selector in the build output
!renderer/mode-selector/
!renderer/mode-selector/**
out
diff --git a/electron-app/package.json b/electron-app/package.json
index 58265f789..55235c394 100644
--- a/electron-app/package.json
+++ b/electron-app/package.json
@@ -6,8 +6,15 @@
"type": "module",
"author": {
"name": "Hyperloop UPV",
- "email": "max766667@gmail.com"
+ "email": "partners@hyperloopupv.com"
},
+ "contributors": [
+ { "name": "Maxim Grivennyy", "email": "max766667@gmail.com" },
+ { "name": "Javier Ribal del Río", "email": "javierribaldelrio@gmail.com" },
+ { "name": "Lola Castello" },
+ { "name": "Alejandro González" },
+ { "name": "Vasyl Klymenko" }
+ ],
"license": "MIT",
"repository": {
"type": "git",
@@ -111,7 +118,8 @@
"linux": {
"target": [
"AppImage",
- "deb"
+ "deb",
+ "rpm"
],
"icon": "icons/512x512.png",
"category": "Utility",
diff --git a/electron-app/renderer/mode-selector/index.html b/electron-app/renderer/mode-selector/index.html
index e554d12fc..871548df1 100644
--- a/electron-app/renderer/mode-selector/index.html
+++ b/electron-app/renderer/mode-selector/index.html
@@ -210,6 +210,7 @@
Control Station
+
@@ -229,6 +230,7 @@
Control Station
}
};
document.getElementById('testing').addEventListener('click', () => sendMode('testing'));
+ document.getElementById('competition').addEventListener('click', () => sendMode('competition'));
document.getElementById('flashing').addEventListener('click', () => sendMode('flashing'));
const appVersionNode = document.getElementById('app-version');
diff --git a/electron-app/src/app/modeSelector.js b/electron-app/src/app/modeSelector.js
index a64d54fcf..8150525b6 100644
--- a/electron-app/src/app/modeSelector.js
+++ b/electron-app/src/app/modeSelector.js
@@ -16,6 +16,7 @@ import { loadView } from "../windows/mainWindow.js";
const VALID_MODES = {
testing: "testing-view",
flashing: "flashing-view",
+ competition: "competition-view",
default: "testing-view",
};
@@ -79,7 +80,7 @@ async function showModeSelector(screenWidth, screenHeight) {
logger.electron.header("Main application window created");
// Start services and only then load the selected view.
- if (view === "testing-view" || view === "flashing-view") {
+ if (view === "testing-view" || view === "flashing-view" || view === "competition-view") {
await startServices(screenWidth, screenHeight, view);
}
@@ -114,8 +115,8 @@ async function showModeSelector(screenWidth, screenHeight) {
* @returns {Promise}
*/
async function startServices(screenWidth, screenHeight, view) {
- // Start backend only for testing view
- if (view === "testing-view") {
+ // Start backend for testing and competition views
+ if (view === "testing-view" || view === "competition-view") {
const logWindow = createLogWindow(screenWidth, screenHeight);
logWindow.show(); // Show the log window
diff --git a/electron-app/src/app/updater.js b/electron-app/src/app/updater.js
index c5a9c5dba..b41ed1b88 100644
--- a/electron-app/src/app/updater.js
+++ b/electron-app/src/app/updater.js
@@ -43,7 +43,9 @@ function setupUpdater() {
});
// Check for updates
- autoUpdater.checkForUpdates();
+ autoUpdater.checkForUpdates().catch((error) => {
+ logger.electron.error("Update check failed:", error.message);
+ });
}
export { setupUpdater };
diff --git a/electron-app/src/ipc/handlers.js b/electron-app/src/ipc/handlers.js
index 5a4198d23..8d40d9a8c 100644
--- a/electron-app/src/ipc/handlers.js
+++ b/electron-app/src/ipc/handlers.js
@@ -15,13 +15,12 @@ import {
readConfig,
writeConfig,
} from "../config/configInstance.js";
-import { getBackendWorkingDir, restartBackend } from "../processes/backend.js";
+import { getBackendWorkingDir } from "../processes/backend.js";
import { logger } from "../utils/logger.js";
import {
getCurrentView,
getMainWindow,
loadView,
- reloadWindow,
} from "../windows/mainWindow.js";
/**
@@ -68,10 +67,7 @@ function setupIpcHandlers() {
ipcMain.handle("save-config", async (event, config) => {
try {
await writeConfig(config);
- await restartBackend();
-
- reloadWindow();
-
+ app.emit("return-to-selector");
return true;
} catch (error) {
logger.electron.error("Error saving config:", error);
@@ -106,10 +102,7 @@ function setupIpcHandlers() {
ipcMain.handle("import-config", async () => {
try {
await importConfig();
- await restartBackend();
-
- reloadWindow();
-
+ app.emit("return-to-selector");
return true;
} catch (error) {
logger.electron.error("Error importing config:", error);
diff --git a/shell.nix b/shell.nix
deleted file mode 100644
index a84c7c6da..000000000
--- a/shell.nix
+++ /dev/null
@@ -1,200 +0,0 @@
-{ pkgs ? import {} }:
-
-let
- # Pin nixpkgs for reproducibility
- pinnedPkgs = import (builtins.fetchTarball {
- url = "https://github.com/NixOS/nixpkgs/archive/nixos-23.11.tar.gz";
- sha256 = "1ndiv385w1qyb3b18vw13991fzb9wg4cl21wglk89grsfsnra41k";
- }) {};
-
- # Use pinned packages by default
- finalPkgs = if pkgs == import {} then pinnedPkgs else pkgs;
-
-in finalPkgs.mkShell {
- name = "hyperloop-h10-dev";
-
- buildInputs = with finalPkgs; [
- # Go development
- go
- gotools
- gopls
- delve
-
- # Node.js development
- nodejs_20
- nodePackages.npm
- nodePackages.typescript
- nodePackages.typescript-language-server
-
- # Python for testing
- python3
-
- # System dependencies
- libpcap
- pkg-config
-
- # Build tools
- gnumake
- gcc
-
- # Development utilities
- git
- ripgrep
- jq
- curl
- wget
- tmux
-
- # Optional productivity tools (can be removed for minimal setup)
- watchman
- ];
-
- shellHook = ''
- # Hyperloop H10 Development Environment
-
- # Set up environment variables
- export GOPATH="$HOME/go"
- export PATH="$GOPATH/bin:$PATH"
- export NODE_ENV="development"
- export CGO_ENABLED=1
-
- # Create command functions for running services in tmux
- ethernet-view() {
- # Check if backend is built
- if [ ! -f "backend/cmd/backend" ]; then
- echo "Building backend first..."
- make backend
- fi
-
- # Kill existing session if it exists
- tmux kill-session -t ethernet-view 2>/dev/null || true
-
- # Create new session
- tmux new-session -d -s ethernet-view -n main
-
- # Start backend in first pane
- tmux send-keys -t ethernet-view:main "cd backend/cmd && ./backend" C-m
-
- # Split horizontally and start ethernet-view
- tmux split-window -t ethernet-view:main -h -p 50
- tmux send-keys -t ethernet-view:main.1 "cd ethernet-view && npm run dev" C-m
-
- # Add status pane at bottom
- tmux split-window -t ethernet-view:main.0 -v -p 20
- tmux send-keys -t ethernet-view:main.2 "echo 'Logs/Status - Press Enter for shell'; read; bash" C-m
-
- # Configure pane titles
- tmux select-pane -t ethernet-view:main.0 -T "Backend"
- tmux select-pane -t ethernet-view:main.1 -T "Ethernet View (http://localhost:5174)"
- tmux select-pane -t ethernet-view:main.2 -T "Logs/Shell"
- tmux set-option -t ethernet-view pane-border-status top
-
- echo "Starting ethernet-view session..."
- echo "Backend running on configured port"
- echo "Ethernet View: http://localhost:5174"
-
- # Attach to session
- tmux attach-session -t ethernet-view
- }
-
- control-station() {
- # Check if backend is built
- if [ ! -f "backend/cmd/backend" ]; then
- echo "Building backend first..."
- make backend
- fi
-
- # Kill existing session if it exists
- tmux kill-session -t control-station 2>/dev/null || true
-
- # Create new session
- tmux new-session -d -s control-station -n main
-
- # Start backend in first pane
- tmux send-keys -t control-station:main "cd backend/cmd && ./backend" C-m
-
- # Split horizontally and start control-station
- tmux split-window -t control-station:main -h -p 50
- tmux send-keys -t control-station:main.1 "cd control-station && npm run dev" C-m
-
- # Add status pane at bottom
- tmux split-window -t control-station:main.0 -v -p 20
- tmux send-keys -t control-station:main.2 "echo 'Logs/Status - Press Enter for shell'; read; bash" C-m
-
- # Configure pane titles
- tmux select-pane -t control-station:main.0 -T "Backend"
- tmux select-pane -t control-station:main.1 -T "Control Station (http://localhost:5173)"
- tmux select-pane -t control-station:main.2 -T "Logs/Shell"
- tmux set-option -t control-station pane-border-status top
-
- echo "Starting control-station session..."
- echo "Backend running on configured port"
- echo "Control Station: http://localhost:5173"
-
- # Attach to session
- tmux attach-session -t control-station
- }
-
- # Export functions so they're available in the shell
- export -f ethernet-view
- export -f control-station
-
- # Ensure clean terminal state
- clear
-
- # Check if dependencies need installation
- check_dependencies() {
- local install_needed=false
-
- if [ ! -d "common-front/node_modules" ]; then
- echo "⚠️ Missing common-front dependencies"
- install_needed=true
- fi
-
- if [ ! -d "control-station/node_modules" ]; then
- echo "⚠️ Missing control-station dependencies"
- install_needed=true
- fi
-
- if [ ! -d "ethernet-view/node_modules" ]; then
- echo "⚠️ Missing ethernet-view dependencies"
- install_needed=true
- fi
-
- if [ "$install_needed" = true ]; then
- echo ""
- echo "Run 'make install' to install all dependencies"
- fi
- }
-
- # Display environment info
- echo "Hyperloop H10 Development Environment"
- echo "====================================="
- echo ""
- echo "Environment:"
- echo " Go version: $(go version | cut -d' ' -f3)"
- echo " Node version: $(node --version)"
- echo " NPM version: $(npm --version)"
- echo ""
- echo "Quick Start:"
- echo " make install - Install all dependencies"
- echo " make all - Build all components"
- echo " ethernet-view - Run backend + Ethernet view in tmux"
- echo " control-station - Run backend + Control station in tmux"
- echo ""
- echo "Individual Commands:"
- echo " make backend - Build backend"
- echo " make common-front - Build common frontend library"
- echo " make control-station - Build control station"
- echo " make ethernet-view - Build ethernet view"
- echo ""
-
- check_dependencies
- '';
-
- # Prevent impurities
- pure = true;
-
- # Additional environment variables for pure builds
- NIX_ENFORCE_PURITY = 1;
-}
\ No newline at end of file