From 1898bec5bbb416cd99965f94031f8e8cce81065c Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Mon, 10 Jun 2024 00:56:42 +0200 Subject: [PATCH 01/20] Improve workflow --- .github/workflows/build_all.yaml | 38 ++++++++++++++++++++++++++++ .github/workflows/build_macos.yaml | 24 +++++++++--------- .github/workflows/build_windows.yaml | 26 +++++++++---------- 3 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/build_all.yaml diff --git a/.github/workflows/build_all.yaml b/.github/workflows/build_all.yaml new file mode 100644 index 0000000..c3ed072 --- /dev/null +++ b/.github/workflows/build_all.yaml @@ -0,0 +1,38 @@ +name: Build all artifacts and make release + +on: workflow_dispatch + +jobs: + prepare: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Get current block + id: getBlockHeight + run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT + - name: Build Windows + uses: ./.github/workflows/build_windows + - name: Build macOS + uses: ./.github/workflows/build_macos + - name: Get Windows Artifacts + if: ${{ always() }} + uses: actions/download-artifact@v4 + with: + name: windows-artifacts + path: windows + - name: Get Job 1 Artifacts + if: ${{ always() }} + uses: actions/download-artifact@v4 + with: + name: macos-artifacts + path: macos + - name: Create release + uses: ncipollo/release-action@v1 + with: + tag: ${{ steps.getBlockHeight.outputs.blockHeight }} + commit: main + name: release-${{ steps.getBlockHeight.outputs.blockHeight }} + artifacts: "macos/**/*.dmg,macos/**/*.zip,windows/**/*.exe" + allowUpdates: true + makeLatest: true diff --git a/.github/workflows/build_macos.yaml b/.github/workflows/build_macos.yaml index 36eae18..1d07fa9 100644 --- a/.github/workflows/build_macos.yaml +++ b/.github/workflows/build_macos.yaml @@ -34,9 +34,9 @@ jobs: - name: Build with PyInstaller run: | pyinstaller BTClockOTA-universal.spec - - name: Get current block - id: getBlockHeight - run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT + # - name: Get current block + # id: getBlockHeight + # run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT - name: Zip the app bundle run: | cd dist @@ -54,12 +54,12 @@ jobs: path: | dist/* !dist/BTClockOTA.app - - name: Create release - uses: ncipollo/release-action@v1 - with: - tag: ${{ steps.getBlockHeight.outputs.blockHeight }} - commit: main - name: release-${{ steps.getBlockHeight.outputs.blockHeight }} - artifacts: "dist/*.dmg,dist/*.zip" - allowUpdates: true - makeLatest: true + # - name: Create release + # uses: ncipollo/release-action@v1 + # with: + # tag: ${{ steps.getBlockHeight.outputs.blockHeight }} + # commit: main + # name: release-${{ steps.getBlockHeight.outputs.blockHeight }} + # artifacts: "dist/*.dmg,dist/*.zip" + # allowUpdates: true + # makeLatest: true diff --git a/.github/workflows/build_windows.yaml b/.github/workflows/build_windows.yaml index cfbdaeb..94899de 100644 --- a/.github/workflows/build_windows.yaml +++ b/.github/workflows/build_windows.yaml @@ -34,21 +34,21 @@ jobs: --volume "${{ github.workspace }}:/src/" \ --env SPECFILE=./BTClockOTA.spec \ batonogov/pyinstaller-windows:latest - - name: Get current block - id: getBlockHeight - run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT + # - name: Get current block + # id: getBlockHeight + # run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT - name: Archive artifacts uses: actions/upload-artifact@v4 with: name: windows-artifacts path: dist/ - - name: Create release - uses: ncipollo/release-action@v1 - with: - tag: ${{ steps.getBlockHeight.outputs.blockHeight }} - commit: main - name: release-${{ steps.getBlockHeight.outputs.blockHeight }} - artifacts: 'dist/**' - allowUpdates: true - removeArtifacts: true - makeLatest: true \ No newline at end of file + # - name: Create release + # uses: ncipollo/release-action@v1 + # with: + # tag: ${{ steps.getBlockHeight.outputs.blockHeight }} + # commit: main + # name: release-${{ steps.getBlockHeight.outputs.blockHeight }} + # artifacts: 'dist/**' + # allowUpdates: true + # removeArtifacts: true + # makeLatest: true \ No newline at end of file From f84e27eeaf703e972dd7961fd9b0b941dda7f7cb Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Mon, 10 Jun 2024 01:00:06 +0200 Subject: [PATCH 02/20] Add extensions --- .github/workflows/build_all.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_all.yaml b/.github/workflows/build_all.yaml index c3ed072..bc7d253 100644 --- a/.github/workflows/build_all.yaml +++ b/.github/workflows/build_all.yaml @@ -12,9 +12,9 @@ jobs: id: getBlockHeight run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT - name: Build Windows - uses: ./.github/workflows/build_windows + uses: ./.github/workflows/build_windows.yml - name: Build macOS - uses: ./.github/workflows/build_macos + uses: ./.github/workflows/build_macos.yml - name: Get Windows Artifacts if: ${{ always() }} uses: actions/download-artifact@v4 From 7872142ea09ee06707d6e05c8bf4689acc72fda3 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Mon, 10 Jun 2024 01:01:25 +0200 Subject: [PATCH 03/20] Checkout repo --- .github/workflows/build_all.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build_all.yaml b/.github/workflows/build_all.yaml index bc7d253..07d1e7a 100644 --- a/.github/workflows/build_all.yaml +++ b/.github/workflows/build_all.yaml @@ -8,6 +8,8 @@ jobs: permissions: contents: write steps: + - name: Checkout repository + uses: actions/checkout@v4 - name: Get current block id: getBlockHeight run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT From 4649feda74dccce4f1e3258cf730cd843a7c07ce Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Mon, 10 Jun 2024 01:04:56 +0200 Subject: [PATCH 04/20] Extension fix --- .github/workflows/build_all.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_all.yaml b/.github/workflows/build_all.yaml index 07d1e7a..291e692 100644 --- a/.github/workflows/build_all.yaml +++ b/.github/workflows/build_all.yaml @@ -14,9 +14,9 @@ jobs: id: getBlockHeight run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT - name: Build Windows - uses: ./.github/workflows/build_windows.yml + uses: ./.github/workflows/build_windows.yaml - name: Build macOS - uses: ./.github/workflows/build_macos.yml + uses: ./.github/workflows/build_macos.yaml - name: Get Windows Artifacts if: ${{ always() }} uses: actions/download-artifact@v4 From a83c50ab418dd14f264fad11ea76109191fb1ad3 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Mon, 10 Jun 2024 01:12:41 +0200 Subject: [PATCH 05/20] Trying it different --- .github/workflows/build_all.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_all.yaml b/.github/workflows/build_all.yaml index 291e692..a5118cb 100644 --- a/.github/workflows/build_all.yaml +++ b/.github/workflows/build_all.yaml @@ -14,9 +14,9 @@ jobs: id: getBlockHeight run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT - name: Build Windows - uses: ./.github/workflows/build_windows.yaml + uses: btclock/ota-flasher/.github/workflows/build_windows.yaml@main - name: Build macOS - uses: ./.github/workflows/build_macos.yaml + uses: btclock/ota-flasher/.github/workflows/build_macos.yaml@main - name: Get Windows Artifacts if: ${{ always() }} uses: actions/download-artifact@v4 From fee2992e8650bccae1d1a4a185062d755de60996 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Mon, 10 Jun 2024 01:20:47 +0200 Subject: [PATCH 06/20] All in one file --- .github/workflows/build_all.yaml | 85 +++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_all.yaml b/.github/workflows/build_all.yaml index a5118cb..af917ca 100644 --- a/.github/workflows/build_all.yaml +++ b/.github/workflows/build_all.yaml @@ -3,20 +3,93 @@ name: Build all artifacts and make release on: workflow_dispatch jobs: - prepare: + build-macos: + runs-on: macos-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Download universal2 Python + run: | + curl -o python.pkg https://www.python.org/ftp/python/3.12.4/python-3.12.4-macos11.pkg + - name: Install Python + run: | + sudo installer -pkg python.pkg -target / + - name: Add Python to PATH + run: | + echo "/Library/Frameworks/Python.framework/Versions/3.12/bin" >> $GITHUB_PATH + - name: Verify Python installation + run: | + python3 --version + pip3 --version + - name: Install dependencies + run: | + pip3 install --upgrade pip + pip3 install pyinstaller + pip3 install --no-cache cffi --no-binary :all: + pip3 install --no-cache charset_normalizer --no-binary :all: + pip3 install -U --pre -f https://wxpython.org/Phoenix/snapshot-builds/ wxPython + pip3 install -r requirements.txt + - name: Build with PyInstaller + run: | + pyinstaller BTClockOTA-universal.spec + - name: Zip the app bundle + run: | + cd dist + zip -r BTClockOTA-macos-universal2.zip BTClockOTA.app + - name: Create DMG + run: | + mkdir dmg_temp + cp -R dist/BTClockOTA.app dmg_temp/ + # Create the DMG file + hdiutil create -volname "BTClockOTA" -srcfolder dmg_temp -ov -format UDZO "dist/BTClockOTA-universal.dmg" + - name: Archive artifacts + uses: actions/upload-artifact@v4 + with: + name: macos-artifacts + path: | + dist/* + !dist/BTClockOTA.app + build-windows: + runs-on: + - ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Cache Docker layers + uses: actions/cache@v4 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + - name: Run Docker Container + run: | + docker run --rm \ + --volume "${{ github.workspace }}:/src/" \ + --env SPECFILE=./BTClockOTA.spec \ + batonogov/pyinstaller-windows:latest + - name: Archive artifacts + uses: actions/upload-artifact@v4 + with: + name: windows-artifacts + path: dist/ + release: + needs: [build-macos, build-windows] runs-on: ubuntu-latest permissions: contents: write steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v4 - name: Get current block id: getBlockHeight run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT - - name: Build Windows - uses: btclock/ota-flasher/.github/workflows/build_windows.yaml@main - - name: Build macOS - uses: btclock/ota-flasher/.github/workflows/build_macos.yaml@main - name: Get Windows Artifacts if: ${{ always() }} uses: actions/download-artifact@v4 From e5bddaa8b149d2ea514acf9dff1fef48ab42e1f6 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Mon, 10 Jun 2024 13:11:10 +0200 Subject: [PATCH 07/20] Add linux build --- .github/workflows/build_linux.yaml | 39 ++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/build_linux.yaml diff --git a/.github/workflows/build_linux.yaml b/.github/workflows/build_linux.yaml new file mode 100644 index 0000000..3f907c6 --- /dev/null +++ b/.github/workflows/build_linux.yaml @@ -0,0 +1,39 @@ +name: Build Linux artifacts + +on: workflow_dispatch + +jobs: + build-linux: + runs-on: + - ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Cache Docker layers + uses: actions/cache@v4 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + - name: Run Docker Container + run: | + docker run --rm \ + --volume "${{ github.workspace }}:/src/" \ + --env SPECFILE=./BTClockOTA.spec \ + ghcr.io/btclock/pyinstaller-wxpython-linux:latest + - name: Archive artifacts + uses: actions/upload-artifact@v4 + with: + name: linux-artifacts + path: dist/ From 6f53026c380f18eaae045cba111bec338595002b Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Mon, 10 Jun 2024 13:23:14 +0200 Subject: [PATCH 08/20] Add build linux to build all --- .github/workflows/build_all.yaml | 47 ++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_all.yaml b/.github/workflows/build_all.yaml index af917ca..2fe9f3b 100644 --- a/.github/workflows/build_all.yaml +++ b/.github/workflows/build_all.yaml @@ -79,8 +79,43 @@ jobs: with: name: windows-artifacts path: dist/ + build-linux: + runs-on: + - ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Cache Docker layers + uses: actions/cache@v4 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + - name: Run Docker Container + run: | + docker run --rm \ + --volume "${{ github.workspace }}:/src/" \ + --env SPECFILE=./BTClockOTA.spec \ + ghcr.io/btclock/pyinstaller-wxpython-linux:latest && + mv dist/BTClockOTA dist/BTClockOTA-linux-amd64 + - name: Archive artifacts + uses: actions/upload-artifact@v4 + with: + name: linux-artifacts + path: dist/ release: - needs: [build-macos, build-windows] + needs: [build-macos, build-windows, build-linux] runs-on: ubuntu-latest permissions: contents: write @@ -96,18 +131,24 @@ jobs: with: name: windows-artifacts path: windows - - name: Get Job 1 Artifacts + - name: Get macOS Artifacts if: ${{ always() }} uses: actions/download-artifact@v4 with: name: macos-artifacts path: macos + - name: Get Linux Artifacts + if: ${{ always() }} + uses: actions/download-artifact@v4 + with: + name: linux-artifacts + path: linux - name: Create release uses: ncipollo/release-action@v1 with: tag: ${{ steps.getBlockHeight.outputs.blockHeight }} commit: main name: release-${{ steps.getBlockHeight.outputs.blockHeight }} - artifacts: "macos/**/*.dmg,macos/**/*.zip,windows/**/*.exe" + artifacts: "macos/**/*.dmg,macos/**/*.zip,windows/**/*.exe,linux/*" allowUpdates: true makeLatest: true From 9fb7fb433f5221746ccedd9899b6e54aaed4b62d Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Mon, 10 Jun 2024 15:31:13 +0200 Subject: [PATCH 09/20] Add console control, bugfixes and windows debug build --- .github/workflows/build_windows.yaml | 2 +- BTClockOTA-debug.spec | 39 ++++++++++++++++++++++++++++ app/espota.py | 17 ++++-------- app/fw_updater.py | 9 ++++++- app/main.py | 32 ++++++++++++++++++++--- app/release_checker.py | 6 +++-- 6 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 BTClockOTA-debug.spec diff --git a/.github/workflows/build_windows.yaml b/.github/workflows/build_windows.yaml index 94899de..2525f74 100644 --- a/.github/workflows/build_windows.yaml +++ b/.github/workflows/build_windows.yaml @@ -32,7 +32,7 @@ jobs: run: | docker run --rm \ --volume "${{ github.workspace }}:/src/" \ - --env SPECFILE=./BTClockOTA.spec \ + --env SPECFILE=./BTClockOTA-debug.spec \ batonogov/pyinstaller-windows:latest # - name: Get current block # id: getBlockHeight diff --git a/BTClockOTA-debug.spec b/BTClockOTA-debug.spec new file mode 100644 index 0000000..82b9de4 --- /dev/null +++ b/BTClockOTA-debug.spec @@ -0,0 +1,39 @@ +# -*- mode: python ; coding: utf-8 -*- + + +a = Analysis( + ['app.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=['zeroconf._utils.ipaddress', 'zeroconf._handlers.answers', 'pyserial', 'wx'], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.datas, + [], + name='BTClockOTA-debug', + debug=True, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=['update-icon.ico'], +) diff --git a/app/espota.py b/app/espota.py index 487f8ed..a55e5cf 100644 --- a/app/espota.py +++ b/app/espota.py @@ -102,8 +102,8 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, inv_tries = 0 data = "" msg = "Sending invitation to %s " % remote_addr - sys.stderr.write(msg) - sys.stderr.flush() + logging.info(msg) + while inv_tries < 10: inv_tries += 1 sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) @@ -111,8 +111,7 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, try: sent = sock2.sendto(message.encode(), remote_address) # noqa: F841 except: # noqa: E722 - sys.stderr.write("failed\n") - sys.stderr.flush() + logging.info("failed\n") sock2.close() logging.error("Host %s Not Found", remote_addr) return 1 @@ -121,11 +120,8 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, data = sock2.recv(37).decode() break except: # noqa: E722 - sys.stderr.write(".") - sys.stderr.flush() +# logging.info(".") sock2.close() - sys.stderr.write("\n") - sys.stderr.flush() if inv_tries == 10: logging.error("No response from the ESP") return 1 @@ -177,8 +173,7 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, if PROGRESS: progress_handler(0) else: - sys.stderr.write("Uploading") - sys.stderr.flush() + logging.info("Uploading") offset = 0 while True: chunk = f.read(1024) @@ -192,7 +187,6 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, res = connection.recv(10) last_response_contained_ok = "OK" in res.decode() except Exception as e: - sys.stderr.write("\n") logging.error("Error Uploading: %s", str(e)) connection.close() return 1 @@ -202,7 +196,6 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, connection.close() return 0 - sys.stderr.write("\n") logging.info("Waiting for result...") count = 0 while count < 5: diff --git a/app/fw_updater.py b/app/fw_updater.py index 1ca37af..ec19d88 100644 --- a/app/fw_updater.py +++ b/app/fw_updater.py @@ -12,8 +12,9 @@ class FwUpdater: update_progress = None currentlyUpdating = False - def __init__(self, update_progress): + def __init__(self, update_progress, event_cb): self.update_progress = update_progress + self.event_cb = event_cb def get_serial_ports(self): ports = serial.tools.list_ports.comports() @@ -76,6 +77,9 @@ class FwUpdater: self.updatingName = address self.currentlyUpdating = True + + if self.event_cb is not None: + self.event_cb("Starting Firmware update") if os.path.exists(os.path.abspath(local_filename)): thread = Thread(target=self.run_fs_update, args=( @@ -88,6 +92,9 @@ class FwUpdater: self.updatingName = address self.currentlyUpdating = True + + if self.event_cb is not None: + self.event_cb("Starting WebUI update") if os.path.exists(os.path.abspath(local_filename)): thread = Thread(target=self.run_fs_update, args=( diff --git a/app/main.py b/app/main.py index 3c0c173..2b79b05 100644 --- a/app/main.py +++ b/app/main.py @@ -1,9 +1,11 @@ import concurrent.futures +import logging import serial from app.gui.action_button_panel import ActionButtonPanel from app.release_checker import ReleaseChecker import wx +import wx.richtext as rt from zeroconf import ServiceBrowser, Zeroconf import os @@ -18,6 +20,19 @@ from app.zeroconf_listener import ZeroconfListener from app.espota import FLASH, SPIFFS +class RichTextCtrlHandler(logging.Handler): + def __init__(self, ctrl): + super().__init__() + self.ctrl = ctrl + + def emit(self, record): + msg = self.format(record) + wx.CallAfter(self.append_text, msg + '\n') + + def append_text(self, text): + self.ctrl.AppendText(text) + self.ctrl.ShowPosition(self.ctrl.GetLastPosition()) + class SerialPortsComboBox(wx.ComboBox): def __init__(self, parent, fw_update): self.fw_update = fw_update @@ -38,13 +53,23 @@ class BTClockOTAUpdater(wx.Frame): self.browser = ServiceBrowser( self.zeroconf, "_http._tcp.local.", self.listener) self.api_handler = ApiHandler() - self.fw_updater = FwUpdater(self.call_progress) + self.fw_updater = FwUpdater(self.call_progress, self.SetStatusText) + panel = wx.Panel(self) + self.log_ctrl = rt.RichTextCtrl(panel, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2) + monospace_font = wx.Font(10, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL) + self.log_ctrl.SetFont(monospace_font) + + handler = RichTextCtrlHandler(self.log_ctrl) + handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) + logging.getLogger().addHandler(handler) + logging.getLogger().setLevel(logging.DEBUG) + + self.device_list = DevicesPanel(panel) vbox = wx.BoxSizer(wx.VERTICAL) - vbox.Add(self.device_list, proportion=2, flag=wx.EXPAND | wx.ALL, border=20) hbox = wx.BoxSizer(wx.HORIZONTAL) @@ -62,10 +87,11 @@ class BTClockOTAUpdater(wx.Frame): self.progress_bar = wx.Gauge(panel, range=100) vbox.Add(self.progress_bar, 0, wx.EXPAND | wx.ALL, 20) + vbox.Add(self.log_ctrl, 1, flag=wx.EXPAND | wx.ALL, border=20) panel.SetSizer(vbox) - self.setup_ui() + wx.CallAfter(self.fetch_latest_release_async) def setup_ui(self): diff --git a/app/release_checker.py b/app/release_checker.py index dcc2d66..c10e04b 100644 --- a/app/release_checker.py +++ b/app/release_checker.py @@ -1,4 +1,5 @@ import json +import logging import os import requests import wx @@ -104,13 +105,14 @@ class ReleaseChecker: def download_file(self, url, release_name): '''Downloads Fimware Files''' local_filename = f"{release_name}_{url.split('/')[-1]}" - response = requests.get(url, stream=True) - total_length = response.headers.get('content-length') + if not os.path.exists("firmware"): os.makedirs("firmware") if os.path.exists(f"firmware/{local_filename}"): return + response = requests.get(url, stream=True) + total_length = response.headers.get('content-length') keep_latest_versions('firmware', 2) if total_length is None: From 504199a8c9261ad4ed3bce8c95c8139426510e45 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Mon, 10 Jun 2024 15:43:08 +0200 Subject: [PATCH 10/20] Add hidden imports --- BTClockOTA-debug.spec | 2 +- BTClockOTA-universal.spec | 2 +- BTClockOTA.spec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BTClockOTA-debug.spec b/BTClockOTA-debug.spec index 82b9de4..4c9cbbf 100644 --- a/BTClockOTA-debug.spec +++ b/BTClockOTA-debug.spec @@ -6,7 +6,7 @@ a = Analysis( pathex=[], binaries=[], datas=[], - hiddenimports=['zeroconf._utils.ipaddress', 'zeroconf._handlers.answers', 'pyserial', 'wx'], + hiddenimports=['zeroconf._utils.ipaddress', 'zeroconf._handlers.answers', 'pyserial', 'wx', 'wx._xml'], hookspath=[], hooksconfig={}, runtime_hooks=[], diff --git a/BTClockOTA-universal.spec b/BTClockOTA-universal.spec index 4a0929c..e3d1c07 100644 --- a/BTClockOTA-universal.spec +++ b/BTClockOTA-universal.spec @@ -7,7 +7,7 @@ a = Analysis( binaries=[], datas=[], hiddenimports=['zeroconf._utils.ipaddress', - 'zeroconf._handlers.answers', 'pyserial', 'wx'], + 'zeroconf._handlers.answers', 'pyserial', 'wx', 'wx._xml'], hookspath=[], hooksconfig={}, runtime_hooks=[], diff --git a/BTClockOTA.spec b/BTClockOTA.spec index 1aec7f3..0f3984b 100644 --- a/BTClockOTA.spec +++ b/BTClockOTA.spec @@ -6,7 +6,7 @@ a = Analysis( pathex=[], binaries=[], datas=[], - hiddenimports=['zeroconf._utils.ipaddress', 'zeroconf._handlers.answers', 'pyserial', 'wx'], + hiddenimports=['zeroconf._utils.ipaddress', 'zeroconf._handlers.answers', 'pyserial', 'wx', 'wx._xml'], hookspath=[], hooksconfig={}, runtime_hooks=[], From 018b0431df2550afd00f2646de40f5cb93b413b3 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Mon, 10 Jun 2024 20:39:39 +0200 Subject: [PATCH 11/20] Improve buttons, use application data dir for cache and downloads --- app.py | 11 ++++++----- app/fw_updater.py | 6 ++++-- app/gui/action_button_panel.py | 2 +- app/main.py | 27 +++++++++++++++++++++------ app/release_checker.py | 14 +++++--------- app/utils.py | 15 ++++++++++++++- 6 files changed, 51 insertions(+), 24 deletions(-) diff --git a/app.py b/app.py index 7e92065..e82a0c9 100644 --- a/app.py +++ b/app.py @@ -1,7 +1,8 @@ +from app.main import BTClockOTAUpdater import wx -from app.main import BTClockOTAUpdater - -app = wx.App(False) -frame = BTClockOTAUpdater(None, 'BTClock OTA updater') -app.MainLoop() \ No newline at end of file +if __name__ == "__main__": + app = wx.App(False) + frame = BTClockOTAUpdater(None, 'BTClock OTA updater') + + app.MainLoop() diff --git a/app/fw_updater.py b/app/fw_updater.py index ec19d88..e4e5d8e 100644 --- a/app/fw_updater.py +++ b/app/fw_updater.py @@ -7,6 +7,8 @@ import esptool import serial import wx +from app.utils import get_app_data_folder + class FwUpdater: update_progress = None @@ -72,7 +74,7 @@ class FwUpdater: if (hw_rev == "REV_B_EPD_2_13"): model_name = "btclock_rev_b_213epd" - local_filename = f"firmware/{ + local_filename = f"{get_app_data_folder()}/{ release_name}_{model_name}_firmware.bin" self.updatingName = address @@ -88,7 +90,7 @@ class FwUpdater: def start_fs_update(self, release_name, address): # Path to the firmware file - local_filename = f"firmware/{release_name}_littlefs.bin" + local_filename = f"{get_app_data_folder()}/{release_name}_littlefs.bin" self.updatingName = address self.currentlyUpdating = True diff --git a/app/gui/action_button_panel.py b/app/gui/action_button_panel.py index 5c28669..bfbf3a2 100644 --- a/app/gui/action_button_panel.py +++ b/app/gui/action_button_panel.py @@ -27,7 +27,7 @@ class ActionButtonPanel(wx.Panel): self.update_button = wx.Button(self, label="Update Firmware") self.update_button.Bind(wx.EVT_BUTTON, self.on_click_update_firmware) - self.update_fs_button = wx.Button(self, label="Update Filesystem") + self.update_fs_button = wx.Button(self, label="Update WebUI") self.update_fs_button.Bind(wx.EVT_BUTTON, self.on_click_update_fs) self.identify_button = wx.Button(self, label="Identify") diff --git a/app/main.py b/app/main.py index 2b79b05..719dcde 100644 --- a/app/main.py +++ b/app/main.py @@ -1,5 +1,6 @@ import concurrent.futures import logging +import traceback import serial from app.gui.action_button_panel import ActionButtonPanel @@ -15,11 +16,14 @@ from app import espota from app.api import ApiHandler from app.fw_updater import FwUpdater from app.gui.devices_panel import DevicesPanel +from app.utils import get_app_data_folder from app.zeroconf_listener import ZeroconfListener from app.espota import FLASH, SPIFFS - +class BTClockOTAApp(wx.App): + def OnInit(self): + return True class RichTextCtrlHandler(logging.Handler): def __init__(self, ctrl): super().__init__() @@ -27,7 +31,7 @@ class RichTextCtrlHandler(logging.Handler): def emit(self, record): msg = self.format(record) - wx.CallAfter(self.append_text, msg + '\n') + wx.CallAfter(self.append_text, "\n" + msg) def append_text(self, text): self.ctrl.AppendText(text) @@ -46,6 +50,7 @@ class BTClockOTAUpdater(wx.Frame): def __init__(self, parent, title): wx.Frame.__init__(self, parent, title=title, size=(800, 500)) + self.SetMinSize((800, 500)) self.releaseChecker = ReleaseChecker() self.zeroconf = Zeroconf() @@ -62,7 +67,7 @@ class BTClockOTAUpdater(wx.Frame): self.log_ctrl.SetFont(monospace_font) handler = RichTextCtrlHandler(self.log_ctrl) - handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) + handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', '%H:%M:%S')) logging.getLogger().addHandler(handler) logging.getLogger().setLevel(logging.DEBUG) @@ -93,7 +98,7 @@ class BTClockOTAUpdater(wx.Frame): self.setup_ui() wx.CallAfter(self.fetch_latest_release_async) - + wx.YieldIfNeeded() def setup_ui(self): self.setup_menubar() self.status_bar = self.CreateStatusBar(2) @@ -102,6 +107,8 @@ class BTClockOTAUpdater(wx.Frame): def setup_menubar(self): filemenu = wx.Menu() + menuOpenDownloadDir = filemenu.Append( + wx.ID_OPEN, "&Open Download Dir", " Open the directory with firmware files and cache") menuAbout = filemenu.Append( wx.ID_ABOUT, "&About", " Information about this program") menuExit = filemenu.Append( @@ -109,8 +116,9 @@ class BTClockOTAUpdater(wx.Frame): menuBar = wx.MenuBar() menuBar.Append(filemenu, "&File") - self.SetMenuBar(menuBar) + self.SetMenuBar(menuBar) + self.Bind(wx.EVT_MENU, self.OnOpenDownloadFolder, menuOpenDownloadDir) self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout) self.Bind(wx.EVT_MENU, self.OnExit, menuExit) @@ -175,6 +183,9 @@ class BTClockOTAUpdater(wx.Frame): def fetch_latest_release_async(self): # Start a new thread to execute fetch_latest_release + app_folder = get_app_data_folder() + if not os.path.exists(app_folder): + os.makedirs(app_folder) executor = concurrent.futures.ThreadPoolExecutor() future = executor.submit(self.releaseChecker.fetch_latest_release) future.add_done_callback(self.handle_latest_release) @@ -186,7 +197,11 @@ class BTClockOTAUpdater(wx.Frame): latest_release}\nCommit: {self.releaseChecker.commit_hash}") except Exception as e: self.fw_label.SetLabel(f"Error occurred: {str(e)}") - + traceback.print_tb(e.__traceback__) + + def OnOpenDownloadFolder(self, e): + wx.LaunchDefaultBrowser(get_app_data_folder()) + def OnAbout(self, e): dlg = wx.MessageDialog( self, "An updater for BTClocks", "About BTClock OTA Updater", wx.OK) diff --git a/app/release_checker.py b/app/release_checker.py index c10e04b..15272dc 100644 --- a/app/release_checker.py +++ b/app/release_checker.py @@ -6,9 +6,9 @@ import wx from typing import Callable from datetime import datetime, timedelta -from app.utils import keep_latest_versions +from app.utils import get_app_data_folder, keep_latest_versions -CACHE_FILE = 'firmware/cache.json' +CACHE_FILE = get_app_data_folder() + '/cache.json' CACHE_DURATION = timedelta(minutes=30) @@ -38,8 +38,6 @@ class ReleaseChecker: cache = self.load_cache() now = datetime.now() - if not os.path.exists("firmware"): - os.makedirs("firmware") if 'latest_release' in cache and (now - datetime.fromisoformat(cache['latest_release']['timestamp'])) < CACHE_DURATION: latest_release = cache['latest_release']['data'] @@ -106,14 +104,12 @@ class ReleaseChecker: '''Downloads Fimware Files''' local_filename = f"{release_name}_{url.split('/')[-1]}" - if not os.path.exists("firmware"): - os.makedirs("firmware") - if os.path.exists(f"firmware/{local_filename}"): + if os.path.exists(f"{get_app_data_folder()}/{local_filename}"): return response = requests.get(url, stream=True) total_length = response.headers.get('content-length') - keep_latest_versions('firmware', 2) + keep_latest_versions(get_app_data_folder(), 2) if total_length is None: raise ReleaseCheckerException("No content length header") @@ -121,7 +117,7 @@ class ReleaseChecker: total_length = int(total_length) chunk_size = 1024 num_chunks = total_length // chunk_size - with open(f"firmware/{local_filename}", 'wb') as f: + with open(f"{get_app_data_folder()}/{local_filename}", 'wb') as f: for i, chunk in enumerate(response.iter_content(chunk_size=chunk_size)): if chunk: f.write(chunk) diff --git a/app/utils.py b/app/utils.py index 8b79405..82559c7 100644 --- a/app/utils.py +++ b/app/utils.py @@ -1,6 +1,6 @@ import os import re -import shutil +import wx def count_versions(folder_path): @@ -30,3 +30,16 @@ def keep_latest_versions(folder_path, num_versions_to_keep=2): for version in versions_to_remove: for file_name in version_files[version]: os.remove(os.path.join(folder_path, file_name)) + +def get_app_data_folder(): + app = wx.GetApp() + if app is None: + app = wx.App(False) + standard_paths = wx.StandardPaths.Get() + app_data_dir = standard_paths.GetAppDocumentsDir() + "/BTClockOTA" + app.Destroy() + return app_data_dir + else: + standard_paths = wx.StandardPaths.Get() + app_data_dir = standard_paths.GetAppDocumentsDir() + "/BTClockOTA" + return app_data_dir \ No newline at end of file From 3cd9fcef46739eb15b27793d8b033f2391dc876c Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sat, 21 Sep 2024 18:51:41 +0200 Subject: [PATCH 12/20] Add more exotic versions to HW detection --- app/fw_updater.py | 11 ++++++++--- app/release_checker.py | 5 ++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/fw_updater.py b/app/fw_updater.py index e4e5d8e..8bd2206 100644 --- a/app/fw_updater.py +++ b/app/fw_updater.py @@ -70,9 +70,14 @@ class FwUpdater: def start_firmware_update(self, release_name, address, hw_rev): # self.SetStatusText(f"Starting firmware update") - model_name = "lolin_s3_mini_213epd" - if (hw_rev == "REV_B_EPD_2_13"): - model_name = "btclock_rev_b_213epd" + hw_rev_to_model = { + "REV_B_EPD_2_13": "btclock_rev_b_213epd", + "REV_V8_EPD_2_13": "btclock_v8_213epd", + "REV_A_EPD_2_9": "lolin_s3_mini_29epd" + } + + model_name = hw_rev_to_model.get(hw_rev, "lolin_s3_mini_213epd") + local_filename = f"{get_app_data_folder()}/{ release_name}_{model_name}_firmware.bin" diff --git a/app/release_checker.py b/app/release_checker.py index 15272dc..032a26c 100644 --- a/app/release_checker.py +++ b/app/release_checker.py @@ -60,7 +60,10 @@ class ReleaseChecker: self.release_name = release_name filenames_to_download = ["lolin_s3_mini_213epd_firmware.bin", - "btclock_rev_b_213epd_firmware.bin", "littlefs.bin"] + "lolin_s3_mini_29epd_firmware.bin", + "btclock_v8_213epd_firmware.bin", + "btclock_rev_b_213epd_firmware.bin", + "littlefs.bin"] asset_urls = [asset['browser_download_url'] for asset in latest_release['assets'] if asset['name'] in filenames_to_download] From 7dfed6af6c012589c4d79ae9db697ca2f095e339 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sun, 17 Nov 2024 21:27:50 -0600 Subject: [PATCH 13/20] Change API to rof.tools mirrors --- app/release_checker.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/app/release_checker.py b/app/release_checker.py index 032a26c..d5c897f 100644 --- a/app/release_checker.py +++ b/app/release_checker.py @@ -11,6 +11,7 @@ from app.utils import get_app_data_folder, keep_latest_versions CACHE_FILE = get_app_data_folder() + '/cache.json' CACHE_DURATION = timedelta(minutes=30) +LATEST_RELEASE_ENDPOINT = "https://git.rof.tools/api/v1/repos/mirrors/btclock_v3/tags" class ReleaseChecker: '''Release Checker for firmware updates''' @@ -34,7 +35,7 @@ class ReleaseChecker: def fetch_latest_release(self): '''Fetch latest firmware release from GitHub''' - repo = "btclock/btclock_v3" + repo = "mirrors/btclock_v3" cache = self.load_cache() now = datetime.now() @@ -42,7 +43,8 @@ class ReleaseChecker: if 'latest_release' in cache and (now - datetime.fromisoformat(cache['latest_release']['timestamp'])) < CACHE_DURATION: latest_release = cache['latest_release']['data'] else: - url = f"https://api.github.com/repos/{repo}/releases/latest" +# url = f"https://api.github.com/repos/{repo}/releases/latest" + url = f"https://git.rof.tools/api/v1/repos/{repo}/releases/latest" try: response = requests.get(url) response.raise_for_status() @@ -72,8 +74,9 @@ class ReleaseChecker: for asset_url in asset_urls: self.download_file(asset_url, release_name) - ref_url = f"https://api.github.com/repos/{ - repo}/git/ref/tags/{release_name}" + ref_url = f"https://git.rof.tools/api/v1/repos/{repo}/tags/{release_name}" + #ref_url = f"https://api.github.com/repos/{ +# repo}/git/ref/tags/{release_name}" if ref_url in cache and (now - datetime.fromisoformat(cache[ref_url]['timestamp'])) < CACHE_DURATION: commit_hash = cache[ref_url]['data'] @@ -81,15 +84,8 @@ class ReleaseChecker: response = requests.get(ref_url) response.raise_for_status() ref_info = response.json() - if ref_info["object"]["type"] == "commit": - commit_hash = ref_info["object"]["sha"] - else: - tag_url = f"https://api.github.com/repos/{ - repo}/git/tags/{ref_info['object']['sha']}" - response = requests.get(tag_url) - response.raise_for_status() - tag_info = response.json() - commit_hash = tag_info["object"]["sha"] + commit_hash = ref_info["commit"]["sha"] + cache[ref_url] = { 'data': commit_hash, 'timestamp': now.isoformat() From c820fb942117ac9c71c50dcd31d3c308579aeabd Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Tue, 26 Nov 2024 01:06:00 +0100 Subject: [PATCH 14/20] Change endpoint to btclock git --- app/release_checker.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/release_checker.py b/app/release_checker.py index d5c897f..f811c94 100644 --- a/app/release_checker.py +++ b/app/release_checker.py @@ -11,7 +11,7 @@ from app.utils import get_app_data_folder, keep_latest_versions CACHE_FILE = get_app_data_folder() + '/cache.json' CACHE_DURATION = timedelta(minutes=30) -LATEST_RELEASE_ENDPOINT = "https://git.rof.tools/api/v1/repos/mirrors/btclock_v3/tags" +LATEST_RELEASE_ENDPOINT = "https://git.btclock.dev/api/v1/repos/btclock/btclock_v3/tags" class ReleaseChecker: '''Release Checker for firmware updates''' @@ -35,7 +35,7 @@ class ReleaseChecker: def fetch_latest_release(self): '''Fetch latest firmware release from GitHub''' - repo = "mirrors/btclock_v3" + repo = "btclock/btclock_v3" cache = self.load_cache() now = datetime.now() @@ -44,7 +44,7 @@ class ReleaseChecker: latest_release = cache['latest_release']['data'] else: # url = f"https://api.github.com/repos/{repo}/releases/latest" - url = f"https://git.rof.tools/api/v1/repos/{repo}/releases/latest" + url = f"https://git.btclock.dev/api/v1/repos/{repo}/releases/latest" try: response = requests.get(url) response.raise_for_status() @@ -74,7 +74,7 @@ class ReleaseChecker: for asset_url in asset_urls: self.download_file(asset_url, release_name) - ref_url = f"https://git.rof.tools/api/v1/repos/{repo}/tags/{release_name}" + ref_url = f"https://git.btclock.dev/api/v1/repos/{repo}/tags/{release_name}" #ref_url = f"https://api.github.com/repos/{ # repo}/git/ref/tags/{release_name}" if ref_url in cache and (now - datetime.fromisoformat(cache[ref_url]['timestamp'])) < CACHE_DURATION: From 46da0c049be779d6eb98b639422d68aaa8bb1893 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Tue, 31 Dec 2024 11:52:58 +0100 Subject: [PATCH 15/20] Update for new filenames --- app/fw_updater.py | 16 ++++++++++++---- app/gui/action_button_panel.py | 3 ++- app/release_checker.py | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/app/fw_updater.py b/app/fw_updater.py index 8bd2206..6ac6b97 100644 --- a/app/fw_updater.py +++ b/app/fw_updater.py @@ -93,18 +93,26 @@ class FwUpdater: address, os.path.abspath(local_filename), FLASH)) thread.start() - def start_fs_update(self, release_name, address): + def start_fs_update(self, release_name, address, hw_rev): + hw_rev_to_model = { + "REV_B_EPD_2_13": "littlefs_8MB", + "REV_V8_EPD_2_13": "littlefs_16MB", + "REV_A_EPD_2_9": "littlefs_4MB" + } + # Path to the firmware file - local_filename = f"{get_app_data_folder()}/{release_name}_littlefs.bin" + local_filename = f"{get_app_data_folder()}/{release_name}_{hw_rev_to_model.get(hw_rev, "littlefs_4MB")}.bin" self.updatingName = address self.currentlyUpdating = True if self.event_cb is not None: - self.event_cb("Starting WebUI update") + self.event_cb(f"Starting WebUI update {local_filename}") if os.path.exists(os.path.abspath(local_filename)): thread = Thread(target=self.run_fs_update, args=( address, os.path.abspath(local_filename), SPIFFS)) thread.start() - + else: + if self.event_cb is not None: + self.event_cb(f"Firmware file not found: {local_filename}") diff --git a/app/gui/action_button_panel.py b/app/gui/action_button_panel.py index bfbf3a2..34e66e2 100644 --- a/app/gui/action_button_panel.py +++ b/app/gui/action_button_panel.py @@ -80,6 +80,7 @@ class ActionButtonPanel(wx.Panel): selected_index = self.device_list.GetFirstSelected() if selected_index != -1: service_name = self.device_list.GetItemText(selected_index, 0) + hw_rev = self.device_list.GetItemText(selected_index, 3) info = self.listener.services.get(service_name) if self.currentlyUpdating: wx.MessageBox("Please wait, already updating", @@ -89,7 +90,7 @@ class ActionButtonPanel(wx.Panel): if info: address = info.parsed_addresses( )[0] if info.parsed_addresses() else "N/A" - self.parent_frame.fw_updater.start_fs_update(self.parent_frame.releaseChecker.release_name, address) + self.parent_frame.fw_updater.start_fs_update(self.parent_frame.releaseChecker.release_name, address, hw_rev) else: wx.MessageBox( "No service information available for selected device", "Error", wx.ICON_ERROR) diff --git a/app/release_checker.py b/app/release_checker.py index f811c94..64b0f79 100644 --- a/app/release_checker.py +++ b/app/release_checker.py @@ -65,7 +65,7 @@ class ReleaseChecker: "lolin_s3_mini_29epd_firmware.bin", "btclock_v8_213epd_firmware.bin", "btclock_rev_b_213epd_firmware.bin", - "littlefs.bin"] + "littlefs_4MB.bin", "littlefs_8MB.bin", "littlefs_16MB.bin"] asset_urls = [asset['browser_download_url'] for asset in latest_release['assets'] if asset['name'] in filenames_to_download] From 86b4b50b9908a681cc5578d02b411eadd86f4594 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Tue, 31 Dec 2024 12:01:50 +0100 Subject: [PATCH 16/20] add forgejo workflow --- .forgejo/workflows/build_all.yaml | 170 ++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 .forgejo/workflows/build_all.yaml diff --git a/.forgejo/workflows/build_all.yaml b/.forgejo/workflows/build_all.yaml new file mode 100644 index 0000000..3e8698d --- /dev/null +++ b/.forgejo/workflows/build_all.yaml @@ -0,0 +1,170 @@ +name: Build all artifacts and make release + +on: + workflow_dispatch: + inputs: + build: + description: 'Select build type' + required: true + default: 'all' + options: + - all + - mac + - windows + - linux + +jobs: + build-macos: + if: ${{ github.event.inputs.build == 'all' || github.event.inputs.build == 'mac' }} + runs-on: macos-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Download universal2 Python + run: | + curl -o python.pkg https://www.python.org/ftp/python/3.12.4/python-3.12.4-macos11.pkg + - name: Install Python + run: | + sudo installer -pkg python.pkg -target / + - name: Add Python to PATH + run: | + echo "/Library/Frameworks/Python.framework/Versions/3.12/bin" >> $GITHUB_PATH + - name: Verify Python installation + run: | + python3 --version + pip3 --version + - name: Install dependencies + run: | + pip3 install --upgrade pip + pip3 install pyinstaller + pip3 install --no-cache cffi --no-binary :all: + pip3 install --no-cache charset_normalizer --no-binary :all: + pip3 install -U --pre -f https://wxpython.org/Phoenix/snapshot-builds/ wxPython + pip3 install -r requirements.txt + - name: Build with PyInstaller + run: | + pyinstaller BTClockOTA-universal.spec + - name: Zip the app bundle + run: | + cd dist + zip -r BTClockOTA-macos-universal2.zip BTClockOTA.app + - name: Create DMG + run: | + mkdir dmg_temp + cp -R dist/BTClockOTA.app dmg_temp/ + # Create the DMG file + hdiutil create -volname "BTClockOTA" -srcfolder dmg_temp -ov -format UDZO "dist/BTClockOTA-universal.dmg" + - name: Archive artifacts + uses: actions/upload-artifact@v4 + with: + name: macos-artifacts + path: | + dist/* + !dist/BTClockOTA.app + build-windows: + if: ${{ github.event.inputs.build == 'all' || github.event.inputs.build == 'windows' }} + runs-on: docker + container: + image: ghcr.io/catthehacker/ubuntu:act-22.04 + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Cache Docker layers + uses: actions/cache@v4 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + - name: Run Docker Container + run: | + docker run --rm \ + --volume "${{ github.workspace }}:/src/" \ + --env SPECFILE=./BTClockOTA.spec \ + batonogov/pyinstaller-windows:latest + - name: Archive artifacts + uses: actions/upload-artifact@v4 + with: + name: windows-artifacts + path: dist/ + build-linux: + if: ${{ github.event.inputs.build == 'all' || github.event.inputs.build == 'linux' }} + runs-on: docker + container: + image: ghcr.io/catthehacker/ubuntu:act-22.04 + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Cache Docker layers + uses: actions/cache@v4 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + - name: Run Docker Container + run: | + docker run --rm \ + --volume "${{ github.workspace }}:/src/" \ + --env SPECFILE=./BTClockOTA.spec \ + ghcr.io/btclock/pyinstaller-wxpython-linux:latest && + mv dist/BTClockOTA dist/BTClockOTA-linux-amd64 + - name: Archive artifacts + uses: actions/upload-artifact@v4 + with: + name: linux-artifacts + path: dist/ + release: + needs: [build-macos, build-windows, build-linux] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Get current block + id: getBlockHeight + run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT + - name: Get Windows Artifacts + if: ${{ always() }} + uses: actions/download-artifact@v4 + with: + name: windows-artifacts + path: windows + - name: Get macOS Artifacts + if: ${{ always() }} + uses: actions/download-artifact@v4 + with: + name: macos-artifacts + path: macos + - name: Get Linux Artifacts + if: ${{ always() }} + uses: actions/download-artifact@v4 + with: + name: linux-artifacts + path: linux + - name: Create release + uses: ncipollo/release-action@v1 + with: + tag: ${{ steps.getBlockHeight.outputs.blockHeight }} + commit: main + name: release-${{ steps.getBlockHeight.outputs.blockHeight }} + artifacts: "macos/**/*.dmg,macos/**/*.zip,windows/**/*.exe,linux/*" + allowUpdates: true + makeLatest: true From 3452a924f964e53d429e48df25bd9443a0280ba4 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Tue, 31 Dec 2024 12:02:53 +0100 Subject: [PATCH 17/20] Fix workflow --- .forgejo/workflows/build_all.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.forgejo/workflows/build_all.yaml b/.forgejo/workflows/build_all.yaml index 3e8698d..3fda450 100644 --- a/.forgejo/workflows/build_all.yaml +++ b/.forgejo/workflows/build_all.yaml @@ -7,6 +7,7 @@ on: description: 'Select build type' required: true default: 'all' + type: choice options: - all - mac From cd5f999cdab718f10c74d6b8f70fb4edfb5a32d6 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Tue, 31 Dec 2024 12:15:13 +0100 Subject: [PATCH 18/20] Use docker images directly instead of dind --- .forgejo/workflows/build_all.yaml | 42 +++++-------------------------- 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/.forgejo/workflows/build_all.yaml b/.forgejo/workflows/build_all.yaml index 3fda450..00d0fa2 100644 --- a/.forgejo/workflows/build_all.yaml +++ b/.forgejo/workflows/build_all.yaml @@ -68,27 +68,15 @@ jobs: if: ${{ github.event.inputs.build == 'all' || github.event.inputs.build == 'windows' }} runs-on: docker container: - image: ghcr.io/catthehacker/ubuntu:act-22.04 + image: batonogov/pyinstaller-windows:latest permissions: contents: write steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Cache Docker layers - uses: actions/cache@v4 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx- - - name: Run Docker Container + - name: Build with PyInstaller run: | - docker run --rm \ - --volume "${{ github.workspace }}:/src/" \ - --env SPECFILE=./BTClockOTA.spec \ - batonogov/pyinstaller-windows:latest + python -m PyInstaller $SPECFILE - name: Archive artifacts uses: actions/upload-artifact@v4 with: @@ -98,33 +86,15 @@ jobs: if: ${{ github.event.inputs.build == 'all' || github.event.inputs.build == 'linux' }} runs-on: docker container: - image: ghcr.io/catthehacker/ubuntu:act-22.04 + image: ghcr.io/btclock/pyinstaller-wxpython-linux:latest permissions: contents: write steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Cache Docker layers - uses: actions/cache@v4 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx- - - name: Run Docker Container + - name: Build with PyInstaller run: | - docker run --rm \ - --volume "${{ github.workspace }}:/src/" \ - --env SPECFILE=./BTClockOTA.spec \ - ghcr.io/btclock/pyinstaller-wxpython-linux:latest && + python -m PyInstaller $SPECFILE && mv dist/BTClockOTA dist/BTClockOTA-linux-amd64 - name: Archive artifacts uses: actions/upload-artifact@v4 From 807d4d05851665860ce7c98e22576553a1b7b825 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Tue, 31 Dec 2024 12:20:20 +0100 Subject: [PATCH 19/20] Add ghcr authentication --- .forgejo/workflows/build_all.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.forgejo/workflows/build_all.yaml b/.forgejo/workflows/build_all.yaml index 00d0fa2..677c8d1 100644 --- a/.forgejo/workflows/build_all.yaml +++ b/.forgejo/workflows/build_all.yaml @@ -87,6 +87,8 @@ jobs: runs-on: docker container: image: ghcr.io/btclock/pyinstaller-wxpython-linux:latest + username: dsbaars + password: ${{ secrets.GH_TOKEN }} permissions: contents: write steps: From c8c69a39b42972a6e57a7897a94879dadc242b07 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Tue, 31 Dec 2024 12:41:11 +0100 Subject: [PATCH 20/20] More workflow fixes --- .forgejo/workflows/build_all.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.forgejo/workflows/build_all.yaml b/.forgejo/workflows/build_all.yaml index 677c8d1..dfdf59e 100644 --- a/.forgejo/workflows/build_all.yaml +++ b/.forgejo/workflows/build_all.yaml @@ -66,7 +66,7 @@ jobs: !dist/BTClockOTA.app build-windows: if: ${{ github.event.inputs.build == 'all' || github.event.inputs.build == 'windows' }} - runs-on: docker + runs-on: docker-amd64 container: image: batonogov/pyinstaller-windows:latest permissions: @@ -84,11 +84,12 @@ jobs: path: dist/ build-linux: if: ${{ github.event.inputs.build == 'all' || github.event.inputs.build == 'linux' }} - runs-on: docker + runs-on: docker-amd64 container: image: ghcr.io/btclock/pyinstaller-wxpython-linux:latest - username: dsbaars - password: ${{ secrets.GH_TOKEN }} + credentials: + username: dsbaars + password: ${{ secrets.GH_TOKEN }} permissions: contents: write steps: