diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 56b1a08..601a19b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,8 +5,6 @@ name: Multi-step multi-platform build on: push: branches: ["main"] - pull_request: - branches: ["main"] jobs: check-version: @@ -56,7 +54,15 @@ jobs: name: EdgeNodeLauncher-WIN32 platformDependencies: "" - - os: ubuntu-latest + - os: ubuntu-24.04 + build: | + chmod +x build_scripts/unix_build.sh + ./build_scripts/unix_build.sh + zip: zip -r EdgeNodeLauncher-LINUX_Ubuntu-24.04.zip dist/* + name: EdgeNodeLauncher-LINUX_Ubuntu-24.04 + platformDependencies: sudo apt-get update && sudo apt install -y '^libxcb.*-dev' libx11-xcb-dev libglu1-mesa-dev libxrender-dev libxi-dev libxkbcommon-dev libxkbcommon-x11-dev --fix-missing + + - os: ubuntu-22.04 build: | chmod +x build_scripts/unix_build.sh ./build_scripts/unix_build.sh @@ -102,6 +108,20 @@ jobs: run: | pip install -r requirements.txt pip3 install --upgrade PyInstaller pyinstaller-hooks-contrib + # Replace the `****************` to MQTT_K in utils/const.py + - name: Replace placeholder in const.py (Windows) + if: runner.os == 'Windows' + env: + MQTT_K: ${{ secrets.MQTT_K }} + run: | + $filePath = "utils/const.py" + (Get-Content $filePath) -replace '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*', "${{ env.MQTT_K }}" | Set-Content $filePath + - name: Replace placeholder in const.py (Linux) + if: runner.os == 'Linux' + env: + MQTT_K: ${{ secrets.MQTT_K }} + run: | + sed -i "s/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/${MQTT_K}/g" utils/const.py - name: Build app run: ${{ matrix.build }} - name: Zip App diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml new file mode 100644 index 0000000..e49420b --- /dev/null +++ b/.github/workflows/pr-build.yml @@ -0,0 +1,141 @@ +name: Multi-step multi-platform build on PR to main branch and saves the artifacts + +on: + pull_request: + branches: ["main"] + +jobs: + check-version: + runs-on: ubuntu-latest + outputs: + should-run: ${{ steps.check-version.outputs.should-run }} + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # Fetches all history for all branches and tags + + - name: Get latest release version + id: latest-version + run: | + LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`) + echo "Latest tag is $LATEST_TAG" + echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV + + - name: Check version in ver.py + id: check-version + run: | + CURRENT_VERSION=$(cat ver.py | grep -o "'.*'") + echo "Current version is $CURRENT_VERSION" + + if [ "$CURRENT_VERSION" == "$LATEST_TAG" ]; then + echo "Version $CURRENT_VERSION has not been incremented vs already released $LATEST_TAG. Build will be cancelled." + echo "::set-output name=should-run::false" + exit 1 + else + echo "Current version $CURRENT_VERSION differs from latest tag $LATEST_TAG. Build will continue." + echo "::set-output name=should-run::true" + fi + + build: + needs: check-version + if: needs.check-version.outputs.should-run == 'true' + + strategy: + matrix: + include: + - os: windows-latest + build: ./build_scripts/win32_build.bat + name: EdgeNodeLauncher-WIN32 + platformDependencies: "" + + - os: ubuntu-24.04 + build: | + chmod +x build_scripts/unix_build.sh + ./build_scripts/unix_build.sh + name: EdgeNodeLauncher-LINUX_Ubuntu-24.04 + platformDependencies: sudo apt-get update && sudo apt install -y '^libxcb.*-dev' libx11-xcb-dev libglu1-mesa-dev libxrender-dev libxi-dev libxkbcommon-dev libxkbcommon-x11-dev --fix-missing + + - os: ubuntu-22.04 + build: | + chmod +x build_scripts/unix_build.sh + ./build_scripts/unix_build.sh + name: EdgeNodeLauncher-LINUX_Ubuntu-22.04 + platformDependencies: sudo apt-get update && sudo apt install -y '^libxcb.*-dev' libx11-xcb-dev libglu1-mesa-dev libxrender-dev libxi-dev libxkbcommon-dev libxkbcommon-x11-dev --fix-missing + + - os: ubuntu-20.04 + build: | + chmod +x build_scripts/unix_build.sh + ./build_scripts/unix_build.sh + name: EdgeNodeLauncher-LINUX_Ubuntu-20.04 + platformDependencies: sudo apt-get update && sudo apt-get install -y '^libxcb.*-dev' libx11-xcb-dev libglu1-mesa-dev libxrender-dev libxi-dev libxkbcommon-dev libxkbcommon-x11-dev --fix-missing + + name: Build installers ${{ matrix.os }} + runs-on: ${{ matrix.os }} + permissions: write-all + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10.11 + uses: actions/setup-python@v2 + with: + python-version: "3.10.11" + - run: ${{ matrix.platformDependencies }} + - name: Setup env + run: | + pip install -r requirements.txt + pip3 install --upgrade PyInstaller pyinstaller-hooks-contrib + + - name: Replace placeholder in const.py (Windows) + if: runner.os == 'Windows' + env: + MQTT_K: ${{ secrets.MQTT_K }} + run: | + $filePath = "utils/const.py" + (Get-Content $filePath) -replace '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*', "${{ env.MQTT_K }}" | Set-Content $filePath + + - name: Replace placeholder in const.py (Linux) + if: runner.os == 'Linux' + env: + MQTT_K: ${{ secrets.MQTT_K }} + run: | + sed -i "s/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/${MQTT_K}/g" utils/const.py + + - name: Build app + run: ${{ matrix.build }} + + - name: Save build artifacts + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.name }}-build-${{ github.event.pull_request.number }}-${{ github.sha }} + path: dist/* + + collect-artifacts: + needs: build + runs-on: ubuntu-latest + permissions: write-all + + steps: + - name: Download build artifacts + uses: actions/download-artifact@v3 + with: + name: EdgeNodeLauncher-WIN32-build-${{ github.event.pull_request.number }}-${{ github.sha }} + path: ./all-builds/EdgeNodeLauncher-WIN32 + + - name: Download build artifacts + uses: actions/download-artifact@v3 + with: + name: EdgeNodeLauncher-LINUX_Ubuntu-24.04-build-${{ github.event.pull_request.number }}-${{ github.sha }} + path: ./all-builds/EdgeNodeLauncher-LINUX_Ubuntu-24.04 + + - name: Download build artifacts + uses: actions/download-artifact@v3 + with: + name: EdgeNodeLauncher-LINUX_Ubuntu-22.04-build-${{ github.event.pull_request.number }}-${{ github.sha }} + path: ./all-builds/EdgeNodeLauncher-LINUX_Ubuntu-22.04 + + - name: Download build artifacts + uses: actions/download-artifact@v3 + with: + name: EdgeNodeLauncher-LINUX_Ubuntu-20.04-build-${{ github.event.pull_request.number }}-${{ github.sha }} + path: ./all-builds/EdgeNodeLauncher-LINUX_Ubuntu-20.04 diff --git a/app_forms/frm_main.py b/app_forms/frm_main.py index 29c06c4..f4803be 100644 --- a/app_forms/frm_main.py +++ b/app_forms/frm_main.py @@ -29,7 +29,7 @@ from PyQt5.QtGui import QFont, QPixmap, QIcon import pyqtgraph as pg - +import services.messaging_service as messaging_service from utils.const import * from utils.docker import _DockerUtilsMixin from utils.updater import _UpdaterMixin @@ -533,7 +533,8 @@ def check_data(self, data): data_size = len(data['timestamps']) if data_size > 0 and data_size > MAX_HISTORY_QUEUE: for key in data: - data[key] = data[key][-MAX_HISTORY_QUEUE:] + if isinstance(data[key], list): + data[key] = data[key][-MAX_HISTORY_QUEUE:] start_time = data['timestamps'][0] end_time = data['timestamps'][-1] self.add_log('Data loaded & cleaned: {} timestamps from {} to {}'.format( @@ -647,22 +648,30 @@ def refresh_local_address(self): address_path = os.path.join(self.volume_path, LOCAL_ADDRESS_FILE) try: with open(address_path, 'r') as file: - address_info = [x for x in file.read().split(' ') if len(x) > 0] - if len(address_info) == 0: - raise FileNotFoundError - if address_info[0] != self.node_addr: - self.node_addr = address_info[0] - self.node_name = address_info[1] if len(address_info) > 1 else '' - str_display = address_info[0][:8] + '...' + address_info[0][-8:] + data = json.load(file) + node_addr = data['address'] + node_alias= data.get('alias', '') + node_eth_addr = data.get('eth_address', '') + node_signature = data.get('signature', '') + self.node_eth_addr = node_eth_addr + self.node_signature = node_signature + if node_addr != self.node_addr: + self.node_addr = node_addr + self.node_name = node_alias + str_display = node_addr[:8] + '...' + node_addr[-8:] self.addressDisplay.setText('Addr: ' + str_display) - self.nameDisplay.setText('Name: ' + address_info[1] if len(address_info) > 1 else '') - self.add_log(f'Local address updated: {self.node_addr} : {self.node_name}') - + self.nameDisplay.setText('Name: ' + node_alias) + self.add_log(f'Local address updated: {self.node_addr} : {self.node_name}, ETH: {self.node_eth_addr}') # endif new address # endwith open except FileNotFoundError: self.addressDisplay.setText('Address file not found.') self.nameDisplay.setText('') + except PermissionError as e: + messaging_service.show_critical_message(self, "Permission Denied", + f"Unable to read the file at {address_path}. Please change the file permissions.") + except Exception as e: + self.add_log(f'Error loading address: {e}', debug=True) return def maybe_refresh_uptime(self): diff --git a/services/messaging_service.py b/services/messaging_service.py new file mode 100644 index 0000000..41d23a2 --- /dev/null +++ b/services/messaging_service.py @@ -0,0 +1,12 @@ +from PyQt5.QtWidgets import QMessageBox + +def show_critical_message(parent, title, message): + """ + Display a critical message box. + + Parameters: + parent (QWidget): The parent widget. + title (str): The title of the message box. + message (str): The message to display. + """ + QMessageBox.critical(parent, title, message) \ No newline at end of file diff --git a/utils/const.py b/utils/const.py index e31351c..5be088d 100644 --- a/utils/const.py +++ b/utils/const.py @@ -17,7 +17,7 @@ LINUX_VOLUME_PATH = '/var/lib/docker/volumes/naeural_vol/_data' LOCAL_HISTORY_FILE = '_data/local_history.json' E2_PEM_FILE = '_data/e2.pem' -LOCAL_ADDRESS_FILE = '_data/local_address.txt' +LOCAL_ADDRESS_FILE = '_data/local_address.json' CONFIG_STARTUP_FILE = 'config_startup.json' CONFIG_APP_FILE = '_data/box_configuration/config_app.txt' ADDRS_FILE = 'authorized_addrs' @@ -46,9 +46,9 @@ AUTO_UPDATE_CHECK_INTERVAL = 60 -DEFAULT_MQTT_HOST = 'r9092118.ala.eu-central-1.emqxsl.com' -DEFAULT_MQTT_USER = 'corenaeural' -DEFAULT_MQTT_PASSWORD = '' +DEFAULT_MQTT_HOST = 'cjkwOTIxMTguYWxhLmV1LWNlbnRyYWwtMS5lbXF4c2wuY29t' +DEFAULT_MQTT_USER = 'Y29yZW5hZXVyYWw=' +DEFAULT_MQTT_PASSWORD = '****************' ENV_TEMPLATE = ''' @@ -69,10 +69,7 @@ # MQTT -EE_MQTT_HOST={} EE_MQTT_PORT=8883 -EE_MQTT_USER={} -EE_MQTT={} EE_MQTT_SUBTOPIC=address EE_MQTT_CERT= diff --git a/utils/docker.py b/utils/docker.py index e09c10b..95f2eb7 100644 --- a/utils/docker.py +++ b/utils/docker.py @@ -3,6 +3,7 @@ import platform import subprocess import platform +import base64 from pathlib import Path from collections import OrderedDict @@ -154,9 +155,9 @@ def __init__(self): self.docker_container_name = DOCKER_CONTAINER_NAME self.docker_tag = DOCKER_TAG self.node_id = self.get_node_id() - self.mqtt_host = DEFAULT_MQTT_HOST - self.mqtt_user = DEFAULT_MQTT_USER - self.mqtt_password = DEFAULT_MQTT_PASSWORD + self.mqtt_host = base64.b64decode(DEFAULT_MQTT_HOST).decode("utf-8") + self.mqtt_user = base64.b64decode(DEFAULT_MQTT_USER).decode("utf-8") + self.mqtt_password = base64.b64decode(DEFAULT_MQTT_PASSWORD).decode("utf-8") self._dev_mode = False self.run_with_sudo = False @@ -226,6 +227,9 @@ def __setup_docker_run(self): self.__CMD += [ '--rm', # remove the container when it exits '--env-file', '.env', #f'"{str(self.env_file)}"', # pass the .env file to the container + '-e', f'EE_MQTT_HOST={self.mqtt_host}', # pass the MQTT host to the container + '-e', f'EE_MQTT_USER={self.mqtt_user}', # pass the MQTT user to the container + '-e', f'EE_MQTT={self.mqtt_password}', # pass the MQTT password to the container '-v', f'{DOCKER_VOLUME}:/edge_node/_local_cache', # mount the volume '--name', self.docker_container_name, '-d', ] @@ -242,9 +246,12 @@ def __setup_docker_run(self): self.__CMD_CLEAN.insert(0, 'sudo') self.__CMD_STOP.insert(0, 'sudo') self.__CMD_INSPECT.insert(0, 'sudo') - + + run_cmd = " ".join(self.get_cmd()) + obfuscated_cmd = run_cmd.replace(self.mqtt_password, '*' * len(self.mqtt_password)) + self.add_log('Docker run command setup complete:') - self.add_log(' - Run: {}'.format(" ".join(self.get_cmd()))) + self.add_log(' - Run: {}'.format(obfuscated_cmd)) self.add_log(' - Clean: {}'.format(" ".join(self.__CMD_CLEAN))) self.add_log(' - Stop: {}'.format(" ".join(self.__CMD_STOP))) self.add_log(' - Inspect: {}'.format(" ".join(self.__CMD_INSPECT))) @@ -256,10 +263,8 @@ def get_cmd(self): result = self.__CMD + ['-p', '80:80', self.docker_image] else: result = self.__CMD + [self.docker_image] - self.add_log("Docker command: '{}'".format(" ".join(result)), debug=True) return result - def get_clean_cmd(self): return self.__CMD_CLEAN @@ -308,23 +313,6 @@ def __check_env_keys(self): except FileNotFoundError: QMessageBox.warning(self, 'Error', '.env file not found.') return False - - # Check if the EE_MQTT key is present and set - if 'EE_MQTT' not in env_vars or not env_vars['EE_MQTT']: - # Prompt the user for the MQTT password - password, ok = QInputDialog.getText(self, 'MQTT Broker password Required', 'Enter MQTT broker password:') - if ok and password: - # Update the env_vars dictionary with the new password - env_vars['EE_MQTT'] = password - # Resave the .env file with the updated key - with open(self.env_file, 'w') as file: - for key, value in env_vars.items(): - file.write(f'{key}={value}\n') - QMessageBox.information(self, 'Success', 'MQTT password set successfully.') - return True - else: - QMessageBox.warning(self, 'Error', 'MQTT password is required to continue.') - return False return True @@ -403,6 +391,7 @@ def is_container_running(self): def launch_container(self): is_env_ok = self.__check_env_keys() if not is_env_ok: + self.add_log('Environment is not ok. Could not start the container.') return self.add_log('Updating image...') self.__maybe_docker_pull() diff --git a/ver.py b/ver.py index ac27694..2567c6e 100644 --- a/ver.py +++ b/ver.py @@ -1 +1 @@ -__VER__ = '0.8.6' +__VER__ = '0.8.7'