feat(ci): publish ESP32 firmware as @virtusco/porter-firmware npm pac… #7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Copyright 2026 VirtusCo | |
| name: Porter Build & Release | |
| on: | |
| push: | |
| branches: [main, prototype] | |
| workflow_dispatch: | |
| defaults: | |
| run: | |
| shell: bash | |
| env: | |
| ZEPHYR_SDK_VERSION: "0.17.0" | |
| ZEPHYR_VERSION: "v4.0.0" | |
| jobs: | |
| version: | |
| name: Version | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.ver.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - id: ver | |
| run: | | |
| VER=$(cat VERSION 2>/dev/null | tr -d '[:space:]' || echo "0.0.0") | |
| echo "version=$VER" >> "$GITHUB_OUTPUT" | |
| build-docker: | |
| name: Build & Publish Docker Image | |
| runs-on: ubuntu-latest | |
| needs: version | |
| permissions: | |
| contents: read | |
| packages: write | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: virtusco/porter-robot | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| lfs: true | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=raw,value=${{ needs.version.outputs.version }} | |
| type=raw,value=latest,enable=${{ github.ref_name == 'main' }} | |
| type=sha,prefix= | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: docker/Dockerfile.prod | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| - name: Save image for release artifacts | |
| run: | | |
| docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.version.outputs.version }} | |
| docker save ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.version.outputs.version }} | gzip > porter-robot-${{ needs.version.outputs.version }}.tar.gz | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: docker-image | |
| path: porter-robot-*.tar.gz | |
| build-esp32: | |
| name: Build ESP32 Firmware & Publish Package | |
| runs-on: ubuntu-latest | |
| needs: version | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| registry-url: 'https://npm.pkg.github.com' | |
| scope: '@virtusco' | |
| - name: Install tools | |
| run: | | |
| pip install west pyelftools intelhex esptool | |
| sudo apt-get update && sudo apt-get install -y cmake ninja-build gperf ccache \ | |
| device-tree-compiler wget xz-utils file make gcc gcc-multilib g++-multilib | |
| - name: West init + update | |
| run: | | |
| cd esp32_firmware | |
| west init -l . | |
| west update --narrow --fetch-opt=--depth=1 | |
| - name: Install Zephyr SDK + ESP32 toolchain | |
| run: | | |
| wget -q "https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${ZEPHYR_SDK_VERSION}/zephyr-sdk-${ZEPHYR_SDK_VERSION}_linux-x86_64_minimal.tar.xz" -O /tmp/sdk.tar.xz | |
| tar xf /tmp/sdk.tar.xz -C ~/ | |
| ~/zephyr-sdk-${ZEPHYR_SDK_VERSION}/setup.sh -c | |
| wget -q "https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${ZEPHYR_SDK_VERSION}/toolchain_linux-x86_64_xtensa-espressif_esp32_zephyr-elf.tar.xz" -O /tmp/tc.tar.xz | |
| tar xf /tmp/tc.tar.xz -C ~/zephyr-sdk-${ZEPHYR_SDK_VERSION}/ | |
| - name: Build motor controller | |
| run: cd esp32_firmware && west build -b esp32_devkitc_wroom motor_controller -d build/motor | |
| - name: Build sensor fusion | |
| run: cd esp32_firmware && west build -b esp32_devkitc_wroom sensor_fusion -d build/sensor | |
| - name: Collect artifacts | |
| run: | | |
| mkdir -p release | |
| cp esp32_firmware/build/motor/zephyr/zephyr.bin release/motor_controller.bin || true | |
| cp esp32_firmware/build/motor/zephyr/zephyr.elf release/motor_controller.elf || true | |
| cp esp32_firmware/build/sensor/zephyr/zephyr.bin release/sensor_fusion.bin || true | |
| cp esp32_firmware/build/sensor/zephyr/zephyr.elf release/sensor_fusion.elf || true | |
| cd release && sha256sum * > SHA256SUMS.txt 2>/dev/null || true | |
| - name: Publish firmware npm package | |
| run: | | |
| cd esp32_firmware/package | |
| mkdir -p bin | |
| cp ../../release/motor_controller.bin bin/ 2>/dev/null || true | |
| cp ../../release/sensor_fusion.bin bin/ 2>/dev/null || true | |
| # Set version from repo VERSION file | |
| VER="${{ needs.version.outputs.version }}" | |
| npm version "$VER" --no-git-tag-version --allow-same-version | |
| npm publish || echo "Publish failed (may already exist at this version)" | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: esp32-firmware | |
| path: release/ | |
| build-flutter: | |
| name: Build Flutter GUI | |
| runs-on: ubuntu-latest | |
| needs: version | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: subosito/flutter-action@v2 | |
| with: | |
| channel: stable | |
| - name: Install Linux build deps | |
| run: | | |
| sudo apt-get update && sudo apt-get install -y \ | |
| libgtk-3-dev libblkid-dev liblzma-dev | |
| - name: Build | |
| run: | | |
| cd src/porter_gui | |
| flutter pub get | |
| flutter build linux --release | |
| - name: Package | |
| run: | | |
| tar czf porter-gui-linux-x64-${{ needs.version.outputs.version }}.tar.gz \ | |
| -C src/porter_gui/build/linux/x64/release bundle/ | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: flutter-gui | |
| path: porter-gui-*.tar.gz | |
| test: | |
| name: Unit Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - run: pip install pytest | |
| - run: pytest tests/unit/ -v | |
| release: | |
| name: GitHub Release | |
| runs-on: ubuntu-latest | |
| needs: [version, build-docker, build-esp32, build-flutter, test] | |
| if: github.event_name == 'push' | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts/ | |
| - name: Generate tag | |
| id: tag | |
| run: | | |
| VER="${{ needs.version.outputs.version }}" | |
| SHA="${GITHUB_SHA:0:7}" | |
| TAG="v${VER}+${SHA}" | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "Release tag: $TAG" | |
| - name: Create release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ steps.tag.outputs.tag }} | |
| name: "Porter Robot v${{ needs.version.outputs.version }} (${{ github.ref_name }})" | |
| generate_release_notes: true | |
| make_latest: ${{ github.ref_name == 'main' }} | |
| prerelease: ${{ github.ref_name != 'main' }} | |
| files: | | |
| artifacts/docker-image/* | |
| artifacts/esp32-firmware/* | |
| artifacts/flutter-gui/* |