chore: upgrade StudioBridge v2.1.2 -> v2.1.3, add persistent-folder creation, update runbook

- Rebuild StudioBridge-2.1.3.jar from latest upstream (v2.1.3) and deploy to portable-dist
- Add ensurePersistentDirectories() so the app creates ~/.studio-bridge and Profiles at startup (GUI + --sendonly)
- Bundle full StudioBridge source under StudioBridge-src/ (with rebuild-jar.bat)
- Update launchers (Launch-StudioBridge.bat, portable-dist/StudioBridge.bat) to v2.1.3
- Add check-update.ps1 and UPDATE.md for the step-by-step update runbook
- Update README (v2.1.3, persistent data + updating sections)
- Ignore StudioBridge-src/target/ build output
This commit is contained in:
ske087
2026-08-06 20:05:21 +03:00
parent 42293e0d5a
commit a50dd3b34b
52 changed files with 5875 additions and 13 deletions
+517
View File
@@ -0,0 +1,517 @@
name: Build and Release
on:
push:
branches: [ "main" ]
tags: [ "v*" ]
env:
APP_NAME: StudioBridge
MAIN_JAR: StudioBridge.jar
MODULES: java.base,java.desktop,java.net.http,java.security.jgss,java.security.sasl,java.naming,jdk.crypto.ec
JAVA_OPTS: "-Xmx512m"
COPYRIGHT: Rdiger-36
VENDOR: Rdiger-36
DESCRIPTION: "With StudioBridge it is possible to make Bambu Lab 3D Printers visible in Bambu Studio or Orca Slicer, that can not be automatically added by them."
ABOUT_URL: https://github.com/Rdiger-36/StudioBridge
WIN_UUID: b062e5bc-0c30-48e7-8f48-9067789d244b
jobs:
# ─────────────────────────────────────────────────────────────────────────
# JAR — cross-platform fat JAR
# Runs on every push to main (CI check) and on tag pushes (release)
# ─────────────────────────────────────────────────────────────────────────
build-jar:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.value }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
cache: 'maven'
- name: Get project version
id: version
run: echo "value=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_OUTPUT
- name: Build fat JAR
run: mvn clean package -q
- name: Prepare JAR artifact
run: |
mkdir input
cp target/*-jar-with-dependencies.jar input/${{ env.MAIN_JAR }}
cp target/*-jar-with-dependencies.jar "target/StudioBridge-${{ steps.version.outputs.value }}.jar"
- uses: actions/upload-artifact@v7
with:
name: jar-release
path: target/StudioBridge-*.jar
- uses: actions/upload-artifact@v7
with:
name: jar-input
path: input/StudioBridge.jar
# ─────────────────────────────────────────────────────────────────────────
# Windows — MSI installer + portable ZIP (x86 and arm64)
# ─────────────────────────────────────────────────────────────────────────
build-windows:
needs: build-jar
if: startsWith(github.ref, 'refs/tags/v')
strategy:
fail-fast: false
matrix:
include:
- runner: windows-latest
arch: x86
- runner: windows-11-arm
arch: arm64
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
# icon.ico is pre-converted and stored in .github/packaging/icons/
- name: Install WiX toolset (ARM64 only — not pre-installed on windows-11-arm)
if: matrix.arch == 'arm64'
shell: pwsh
run: |
choco install wixtoolset --no-progress -y
$wixDir = Get-ChildItem "C:\Program Files (x86)\WiX Toolset*\bin" |
Select-Object -First 1 -ExpandProperty FullName
echo "$wixDir" >> $env:GITHUB_PATH
- name: Download JAR
uses: actions/download-artifact@v7
with:
name: jar-input
path: input
- name: Build minimal runtime via jlink
shell: pwsh
run: |
jlink `
--add-modules "${{ env.MODULES }}" `
--strip-debug `
--no-header-files `
--no-man-pages `
--compress=zip-6 `
--output runtime
- name: Build MSI installer
shell: pwsh
run: |
# Add icon to CLI properties
Copy-Item .github/packaging/cli-win.properties cli-win.properties
Add-Content cli-win.properties "icon=.github/packaging/icons/icon.ico"
jpackage `
--type msi `
--name "${{ env.APP_NAME }}" `
--app-version "${{ needs.build-jar.outputs.version }}" `
--input input `
--main-jar "${{ env.MAIN_JAR }}" `
--icon .github/packaging/icons/icon.ico `
--runtime-image runtime `
--java-options "${{ env.JAVA_OPTS }}" `
--copyright "${{ env.COPYRIGHT }}" `
--vendor "${{ env.VENDOR }}" `
--description "${{ env.DESCRIPTION }}" `
--about-url "${{ env.ABOUT_URL }}" `
--win-menu --win-shortcut `
--win-dir-chooser --win-shortcut-prompt `
--win-upgrade-uuid "${{ env.WIN_UUID }}" `
--add-launcher "StudioBridgeCLI=cli-win.properties" `
--dest msi-output
- name: Build portable app-image
shell: pwsh
run: |
Copy-Item .github/packaging/cli-win.properties cli-win.properties
Add-Content cli-win.properties "icon=.github/packaging/icons/icon.ico"
jpackage `
--type app-image `
--name "${{ env.APP_NAME }}" `
--app-version "${{ needs.build-jar.outputs.version }}" `
--input input `
--main-jar "${{ env.MAIN_JAR }}" `
--icon .github/packaging/icons/icon.ico `
--runtime-image runtime `
--java-options "${{ env.JAVA_OPTS }}" `
--add-launcher "StudioBridgeCLI=cli-win.properties" `
--dest app-image
- name: Create portable ZIP
shell: pwsh
run: |
$v = "${{ needs.build-jar.outputs.version }}"
Compress-Archive `
-Path "app-image\${{ env.APP_NAME }}\*" `
-DestinationPath "StudioBridge-${v}_win_${{ matrix.arch }}_NO-INSTALL.zip"
- name: Rename and move MSI
shell: pwsh
run: |
$v = "${{ needs.build-jar.outputs.version }}"
Get-ChildItem msi-output\*.msi |
Move-Item -Destination "StudioBridge-${v}_win_${{ matrix.arch }}.msi"
- uses: actions/upload-artifact@v7
with:
name: windows-${{ matrix.arch }}
path: |
*.msi
*.zip
# ─────────────────────────────────────────────────────────────────────────
# Linux — AppImage (x86_64 and aarch64)
# ─────────────────────────────────────────────────────────────────────────
build-linux:
needs: build-jar
if: startsWith(github.ref, 'refs/tags/v')
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-latest
arch: x86_64
- runner: ubuntu-24.04-arm
arch: aarch64
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
- name: Install dependencies
run: sudo apt-get install -y libfuse2
- name: Download appimagetool
run: |
wget -q \
"https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${{ matrix.arch }}.AppImage" \
-O appimagetool
chmod +x appimagetool
- name: Download JAR
uses: actions/download-artifact@v7
with:
name: jar-input
path: input
- name: Build minimal runtime via jlink
run: |
jlink \
--add-modules "${{ env.MODULES }}" \
--strip-debug \
--no-header-files \
--no-man-pages \
--compress=zip-6 \
--output runtime
- name: Build app-image via jpackage
run: |
jpackage \
--type app-image \
--name "${{ env.APP_NAME }}" \
--app-version "${{ needs.build-jar.outputs.version }}" \
--input input \
--main-jar "${{ env.MAIN_JAR }}" \
--icon src/main/resources/icon.png \
--runtime-image runtime \
--java-options "${{ env.JAVA_OPTS }}"
- name: Package as AppImage
run: |
VERSION="${{ needs.build-jar.outputs.version }}"
APPDIR="build/${{ env.APP_NAME }}.AppDir"
# FHS-compliant AppDir structure
mkdir -p \
"${APPDIR}/usr/bin" \
"${APPDIR}/usr/lib/${{ env.APP_NAME }}" \
"${APPDIR}/usr/share/applications" \
"${APPDIR}/usr/share/icons/hicolor/256x256/apps"
# Copy jpackage app-image into usr/lib
cp -a "${{ env.APP_NAME }}/." "${APPDIR}/usr/lib/${{ env.APP_NAME }}/"
# Symlink launcher into PATH
ln -sfn "../lib/${{ env.APP_NAME }}/bin/${{ env.APP_NAME }}" \
"${APPDIR}/usr/bin/${{ env.APP_NAME }}"
# AppRun
cat > "${APPDIR}/AppRun" << 'APPRUN'
#!/bin/sh
HERE="$(dirname "$(readlink -f "$0")")"
exec "$HERE/usr/bin/StudioBridge" "$@"
APPRUN
chmod +x "${APPDIR}/AppRun"
# .desktop entry
cat > "${APPDIR}/${{ env.APP_NAME }}.desktop" << 'DESKTOP'
[Desktop Entry]
Type=Application
Name=StudioBridge
Exec=StudioBridge
Icon=StudioBridge
Categories=Utility;
Terminal=false
DESKTOP
# Icons
install -m 0644 src/main/resources/icon.png "${APPDIR}/${{ env.APP_NAME }}.png"
install -m 0644 src/main/resources/icon.png \
"${APPDIR}/usr/share/icons/hicolor/256x256/apps/${{ env.APP_NAME }}.png"
ARCH=${{ matrix.arch }} APPIMAGE_EXTRACT_AND_RUN=1 \
./appimagetool "${APPDIR}" \
"${{ env.APP_NAME }}-${VERSION}-${{ matrix.arch }}.AppImage"
- uses: actions/upload-artifact@v7
with:
name: linux-${{ matrix.arch }}
path: "*.AppImage"
# ─────────────────────────────────────────────────────────────────────────
# macOS — signed + notarized DMG (arm64 / Apple Silicon only)
#
# Intel (x86) support dropped: macOS 27 (September 2026) removes Rosetta 2,
# making x86 app execution on Apple Silicon impossible. Intel Macs cannot
# upgrade to macOS 27, so the x86 user base will effectively be zero.
#
# Required secrets:
# MACOS_CERTIFICATE — base64-encoded Developer ID Application .p12
# MACOS_CERTIFICATE_PWD — export password for the .p12
# MACOS_KEYCHAIN_PWD — temporary keychain password (any value)
# APPLE_TEAM_NAME — name part of "Developer ID Application: Name (ID)"
# APPLE_TEAM_ID — 10-character Apple Team ID
# APPLE_ID — Apple ID e-mail for notarytool
# APPLE_APP_PASSWORD — app-specific password (appleid.apple.com)
# ─────────────────────────────────────────────────────────────────────────
build-macos:
needs: build-jar
if: startsWith(github.ref, 'refs/tags/v')
runs-on: macos-latest # arm64 — Apple Silicon (M-Series)
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
- name: Import Developer ID certificate
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
MACOS_KEYCHAIN_PWD: ${{ secrets.MACOS_KEYCHAIN_PWD }}
run: |
echo "$MACOS_CERTIFICATE" | base64 --decode > certificate.p12
security create-keychain -p "$MACOS_KEYCHAIN_PWD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$MACOS_KEYCHAIN_PWD" build.keychain
security import certificate.p12 \
-k build.keychain \
-P "$MACOS_CERTIFICATE_PWD" \
-T /usr/bin/codesign \
-T /usr/bin/jpackage
security set-key-partition-list \
-S apple-tool:,apple: \
-s -k "$MACOS_KEYCHAIN_PWD" build.keychain
# icon.icns is pre-converted and stored in .github/packaging/icons/
- name: Download JAR
uses: actions/download-artifact@v7
with:
name: jar-input
path: input
- name: Build minimal runtime via jlink
run: |
jlink \
--add-modules "${{ env.MODULES }}" \
--strip-debug \
--no-header-files \
--no-man-pages \
--compress=zip-6 \
--output runtime
- name: Sign all native libraries inside JAR
# Apple notarization scans inside JARs for .dylib/.jnilib and rejects unsigned ones.
# Multiple dependencies (JNA, FlatLaf, ...) bundle macOS native code — sign them all.
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_TEAM_NAME: ${{ secrets.APPLE_TEAM_NAME }}
run: |
mkdir -p jar-natives
# Collect paths of all native libs inside the JAR
NATIVE_LIBS=$(jar tf input/StudioBridge.jar | grep -E '\.(jnilib|dylib)$')
if [ -z "$NATIVE_LIBS" ]; then
echo "No native libs found in JAR — skipping."
else
# Extract only those files
(cd jar-natives && echo "$NATIVE_LIBS" | xargs jar xf ../input/StudioBridge.jar)
# Sign each one with hardened runtime + secure timestamp
find jar-natives -name "*.jnilib" -o -name "*.dylib" | \
while read lib; do
echo "Signing: $lib"
codesign --force \
--sign "Developer ID Application: ${APPLE_TEAM_NAME} (${APPLE_TEAM_ID})" \
--options runtime \
--timestamp \
--entitlements .github/entitlements.plist \
"$lib"
done
# Repack signed natives back into the JAR
(cd jar-natives && find . -name "*.jnilib" -o -name "*.dylib" | \
sed 's|^\./||' | xargs jar uf ../input/StudioBridge.jar)
fi
rm -rf jar-natives
- name: Build signed DMG
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_TEAM_NAME: ${{ secrets.APPLE_TEAM_NAME }}
run: |
# Add icon to CLI properties
cp .github/packaging/cli-mac.properties cli-mac.properties
echo "icon=.github/packaging/icons/icon.icns" >> cli-mac.properties
jpackage \
--type dmg \
--name "${{ env.APP_NAME }}" \
--app-version "${{ needs.build-jar.outputs.version }}" \
--input input \
--main-jar "${{ env.MAIN_JAR }}" \
--icon .github/packaging/icons/icon.icns \
--runtime-image runtime \
--java-options "${{ env.JAVA_OPTS }}" \
--copyright "${{ env.COPYRIGHT }}" \
--vendor "${{ env.VENDOR }}" \
--description "${{ env.DESCRIPTION }}" \
--mac-sign \
--mac-signing-key-user-name "Developer ID Application: ${APPLE_TEAM_NAME} (${APPLE_TEAM_ID})" \
--mac-entitlements .github/entitlements.plist \
--add-launcher "StudioBridgeCLI=cli-mac.properties" \
--dest dmg-output
- name: Rename DMG
run: |
V="${{ needs.build-jar.outputs.version }}"
mv dmg-output/*.dmg "StudioBridge-${V}_macOS_arm64.dmg"
- name: Notarize and staple DMG
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
run: |
DMG=$(ls *.dmg | head -1)
# Submit and capture result as JSON for error logging
RESULT=$(xcrun notarytool submit "$DMG" \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_PASSWORD" \
--wait --output-format json)
echo "$RESULT"
STATUS=$(echo "$RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
ID=$(echo "$RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
# On failure print the detailed log from Apple before exiting
if [ "$STATUS" != "Accepted" ]; then
echo "Notarization failed (status: $STATUS) — fetching log..."
xcrun notarytool log "$ID" \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_PASSWORD"
exit 1
fi
xcrun stapler staple "$DMG"
- uses: actions/upload-artifact@v7
with:
name: macos-arm64
path: "*.dmg"
# ─────────────────────────────────────────────────────────────────────────
# Release — collects all artifacts, creates GitHub Release on tag push
# ─────────────────────────────────────────────────────────────────────────
release:
needs: [ build-jar, build-windows, build-linux, build-macos ]
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
steps:
- name: Download all artifacts
uses: actions/download-artifact@v7
with:
path: artifacts
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
artifacts/jar-release/*
artifacts/windows-x86/*
artifacts/windows-arm64/*
artifacts/linux-x86_64/*
artifacts/linux-aarch64/*
artifacts/macos-arm64/*
name: "Version ${{ needs.build-jar.outputs.version }}"
generate_release_notes: true
append_body: true
body: |
---
## Downloads
### Windows
| | Installer (MSI) | Portable (ZIP) |
|---|---|---|
| **x86 (Intel/AMD)** | [⬇ StudioBridge-${{ needs.build-jar.outputs.version }}_win_x86.msi](https://github.com/Rdiger-36/StudioBridge/releases/download/${{ github.ref_name }}/StudioBridge-${{ needs.build-jar.outputs.version }}_win_x86.msi) | [⬇ NO-INSTALL x86](https://github.com/Rdiger-36/StudioBridge/releases/download/${{ github.ref_name }}/StudioBridge-${{ needs.build-jar.outputs.version }}_win_x86_NO-INSTALL.zip) |
| **ARM64** | [⬇ StudioBridge-${{ needs.build-jar.outputs.version }}_win_arm64.msi](https://github.com/Rdiger-36/StudioBridge/releases/download/${{ github.ref_name }}/StudioBridge-${{ needs.build-jar.outputs.version }}_win_arm64.msi) | [⬇ NO-INSTALL arm64](https://github.com/Rdiger-36/StudioBridge/releases/download/${{ github.ref_name }}/StudioBridge-${{ needs.build-jar.outputs.version }}_win_arm64_NO-INSTALL.zip) |
### Linux
| | AppImage |
|---|---|
| **x86_64 (Intel/AMD)** | [⬇ StudioBridge-${{ needs.build-jar.outputs.version }}-x86_64.AppImage](https://github.com/Rdiger-36/StudioBridge/releases/download/${{ github.ref_name }}/StudioBridge-${{ needs.build-jar.outputs.version }}-x86_64.AppImage) |
| **aarch64 (ARM)** | [⬇ StudioBridge-${{ needs.build-jar.outputs.version }}-aarch64.AppImage](https://github.com/Rdiger-36/StudioBridge/releases/download/${{ github.ref_name }}/StudioBridge-${{ needs.build-jar.outputs.version }}-aarch64.AppImage) |
### macOS
| | DMG |
|---|---|
| **Apple Silicon (M-Series)** | [⬇ StudioBridge-${{ needs.build-jar.outputs.version }}_macOS_arm64.dmg](https://github.com/Rdiger-36/StudioBridge/releases/download/${{ github.ref_name }}/StudioBridge-${{ needs.build-jar.outputs.version }}_macOS_arm64.dmg) |
### JAR (alle Plattformen, erfordert Java 21+)
| | |
|---|---|
| **Standalone JAR** | [⬇ StudioBridge-${{ needs.build-jar.outputs.version }}.jar](https://github.com/Rdiger-36/StudioBridge/releases/download/${{ github.ref_name }}/StudioBridge-${{ needs.build-jar.outputs.version }}.jar) |
prerelease: ${{ contains(github.ref_name, '-') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}