@@ -16,7 +16,7 @@ permissions:
1616 contents : read
1717
1818jobs :
19- build :
19+ build_windows :
2020 runs-on : ' windows-latest'
2121 strategy :
2222 matrix :
@@ -246,23 +246,116 @@ jobs:
246246 path : macos/Output/*.dmg
247247 retention-days : 7
248248
249+ build_linux :
250+ runs-on : ' ubuntu-20.04' # Use a consistent Linux environment for building the AppImage
251+ strategy :
252+ matrix :
253+ python-version : ['3.14']
254+
255+ permissions :
256+ contents : write
257+ id-token : write
258+
259+ steps :
260+ - name : Harden the runner (Audit all outbound calls)
261+ uses : step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
262+ with :
263+ egress-policy : audit
264+
265+ - name : Checkout
266+ uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
267+
268+ - name : Install system dependencies for tkinter
269+ run : |
270+ sudo apt-get update -qq
271+ sudo apt-get install -y --no-install-recommends \
272+ python3-tk \
273+ tk-dev \
274+ tcl-dev \
275+ squashfs-tools
276+
277+ - name : Set up Python
278+ id : setup-python
279+ uses : actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
280+ with :
281+ python-version : ${{ matrix.python-version }}
282+
283+ - name : Install dependencies
284+ run : |
285+ python -m pip install --upgrade pip
286+ python -m pip install .[linux_dist]
287+
288+ - name : Verify tkinter is available
289+ run : |
290+ python - <<'PY'
291+ import tkinter
292+ print(f"tkinter OK, TkVersion={tkinter.TkVersion}")
293+ PY
294+
295+ - name : Write the git commit hash to file
296+ run : |
297+ git rev-parse HEAD | tr -d '\n' > git_hash.txt
298+
299+ - name : Build Linux app with PyInstaller
300+ run : |
301+ pyinstaller --clean linux/ardupilot_methodic_configurator.spec
302+
303+ - name : Build AppImage
304+ run : |
305+ set -euo pipefail
306+ VERSION=$(python -c "import ardupilot_methodic_configurator; print(ardupilot_methodic_configurator.__version__)")
307+ APP_NAME="ardupilot_methodic_configurator"
308+ APPIMAGE_NAME="${APP_NAME}_${VERSION}_linux_setup.AppImage"
309+ APPDIR="linux/AppDir"
310+ OUTPUT_DIR="linux/Output"
311+
312+ # Create AppDir structure
313+ rm -rf "$APPDIR"
314+ mkdir -p "$APPDIR" "$OUTPUT_DIR"
315+
316+ # Copy PyInstaller output
317+ cp -R "dist/${APP_NAME}" "$APPDIR/"
318+
319+ # Copy desktop file and icon
320+ cp "linux/${APP_NAME}.desktop" "$APPDIR/"
321+ cp "ardupilot_methodic_configurator/images/ArduPilot_icon.png" "$APPDIR/${APP_NAME}.png"
322+
323+ # Copy AppRun and make executable
324+ cp "linux/AppRun" "$APPDIR/AppRun"
325+ chmod +x "$APPDIR/AppRun"
326+
327+ # Download appimagetool (use --appimage-extract-and-run since FUSE is unavailable in CI)
328+ # Pinned to 1.9.1 for security and reproducibility
329+ wget -q "https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-x86_64.AppImage" \
330+ -O appimagetool
331+ chmod +x appimagetool
332+
333+ ARCH=x86_64 ./appimagetool --appimage-extract-and-run "$APPDIR" "$OUTPUT_DIR/$APPIMAGE_NAME"
334+
335+ - name : Upload Linux build artifacts
336+ uses : actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
337+ with :
338+ name : linux-appimage
339+ path : linux/Output/*.AppImage
340+ retention-days : 7
341+
249342 # Generate SLSA provenance using the official generic workflow
250343# provenance:
251- # needs: [build ]
344+ # needs: [build_windows ]
252345# permissions:
253346# actions: read # To read the workflow path
254347# id-token: write # To sign the provenance
255348# contents: write # To add assets to a release
256349# uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@4876e96b8268fd8b7b8d8574718d06c0d0426d40 # latest commit
257350# with:
258- # base64-subjects: "${{ needs.build .outputs.hashes }}"
351+ # base64-subjects: "${{ needs.build_windows .outputs.hashes }}"
259352# upload-assets: ${{ startsWith(github.ref, 'refs/tags/v') }} # Only upload to releases for v* tags
260353# continue-on-error: false # Explicit error handling - fail fast for security issues
261354
262355 # Release job that depends on provenance generation
263356 release :
264- # needs: [build , provenance]
265- needs : [build , build_macos]
357+ # needs: [build_windows , provenance]
358+ needs : [build_windows , build_macos, build_linux ]
266359 runs-on : windows-latest
267360 if : startsWith(github.ref, 'refs/tags/v') || github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
268361 permissions :
@@ -277,7 +370,7 @@ jobs:
277370 - name : Checkout
278371 uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
279372
280- - name : Download build artifacts
373+ - name : Download Windows build artifacts
281374 uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
282375 with :
283376 name : windows-installer
@@ -289,16 +382,24 @@ jobs:
289382 name : macos-dmg
290383 path : release-artifacts
291384
385+ - name : Download Linux build artifacts
386+ uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
387+ with :
388+ name : linux-appimage
389+ path : release-artifacts
390+
292391 - name : Rename installer for pre-release
293392 if : github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
294393 shell : pwsh
295394 run : |
296- $files = Get-ChildItem release-artifacts\*.* | Where-Object { $_.Extension -in '.exe', '.dmg' }
395+ $files = Get-ChildItem release-artifacts\*.* | Where-Object { $_.Extension -in '.exe', '.dmg', '.AppImage' }
297396 foreach ($file in $files) {
298397 if ($file.Extension -eq '.exe') {
299398 $newName = $file.Name -replace 'ardupilot_methodic_configurator_.*_windows_setup\.exe$', 'ardupilot_methodic_configurator_latest_pre_release_windows_setup.exe'
300399 } elseif ($file.Extension -eq '.dmg') {
301400 $newName = $file.Name -replace 'ardupilot_methodic_configurator_.*_macos_setup\.dmg$', 'ardupilot_methodic_configurator_latest_pre_release_macos_setup.dmg'
401+ } elseif ($file.Extension -eq '.AppImage') {
402+ $newName = $file.Name -replace 'ardupilot_methodic_configurator_.*_linux_setup\.AppImage$', 'ardupilot_methodic_configurator_latest_pre_release_linux_setup.AppImage'
302403 } else {
303404 continue
304405 }
0 commit comments