Release v0.2.4 #16
Workflow file for this run
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
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| permissions: | |
| contents: write | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| build-desktop: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Windows x86_64 | |
| - os: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| client: windows | |
| archive: zip | |
| ext: .exe | |
| # macOS x86_64 (Intel) | |
| - os: macos-latest | |
| target: x86_64-apple-darwin | |
| client: mac | |
| archive: tar.gz | |
| ext: "" | |
| # macOS aarch64 (Apple Silicon) | |
| - os: macos-latest | |
| target: aarch64-apple-darwin | |
| client: mac | |
| archive: tar.gz | |
| ext: "" | |
| # Linux x86_64 | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| client: linux | |
| archive: tar.gz | |
| ext: "" | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Install Linux dependencies | |
| if: matrix.os == 'ubuntu-latest' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libxdo-dev \ | |
| libx11-dev \ | |
| libxrandr-dev \ | |
| libxi-dev \ | |
| libxtst-dev \ | |
| libglib2.0-dev \ | |
| libgtk-3-dev \ | |
| libayatana-appindicator3-dev \ | |
| libxcb-shape0-dev \ | |
| libxcb-xfixes0-dev \ | |
| libdbus-1-dev \ | |
| libasound2-dev | |
| - name: Build | |
| working-directory: ${{ matrix.client }} | |
| run: cargo build --release --target ${{ matrix.target }} | |
| - name: Determine archive name | |
| id: archive | |
| shell: bash | |
| run: | | |
| version="${GITHUB_REF_NAME}" | |
| arch="${{ matrix.target }}" | |
| name="screenmcp-${{ matrix.client }}-${arch}-${version}" | |
| echo "name=${name}" >> "$GITHUB_OUTPUT" | |
| echo "version=${version}" >> "$GITHUB_OUTPUT" | |
| - name: Sign Windows binary | |
| if: matrix.client == 'windows' && env.CODE_SIGNING_PFX != '' | |
| env: | |
| CODE_SIGNING_PFX: ${{ secrets.CODE_SIGNING_PFX }} | |
| CODE_SIGNING_PASSWORD: ${{ secrets.CODE_SIGNING_PASSWORD }} | |
| shell: pwsh | |
| run: | | |
| $pfxBytes = [Convert]::FromBase64String($env:CODE_SIGNING_PFX) | |
| [IO.File]::WriteAllBytes("$pwd\codesign.pfx", $pfxBytes) | |
| $exe = "${{ matrix.client }}\target\${{ matrix.target }}\release\screenmcp-${{ matrix.client }}.exe" | |
| $signtool = Get-ChildItem -Path "C:\Program Files (x86)\Windows Kits" -Recurse -Filter signtool.exe | Where-Object { $_.FullName -like "*x64*" } | Select-Object -First 1 -ExpandProperty FullName | |
| & $signtool sign /f codesign.pfx /p $env:CODE_SIGNING_PASSWORD /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 $exe | |
| Remove-Item codesign.pfx | |
| - name: Package macOS (.app + .dmg) | |
| if: matrix.client == 'mac' | |
| shell: bash | |
| run: | | |
| APP="ScreenMCP.app" | |
| BINARY="mac/target/${{ matrix.target }}/release/screenmcp-mac" | |
| # Create .app bundle | |
| mkdir -p "${APP}/Contents/MacOS" "${APP}/Contents/Resources" | |
| cp mac/Info.plist "${APP}/Contents/Info.plist" | |
| cp "$BINARY" "${APP}/Contents/MacOS/screenmcp-mac" | |
| chmod +x "${APP}/Contents/MacOS/screenmcp-mac" | |
| echo "APPLscmc" > "${APP}/Contents/PkgInfo" | |
| # Build .icns from pre-made PNGs | |
| ICON_DIR="mac/assets/icon-pngs" | |
| if [ -d "$ICON_DIR" ]; then | |
| mkdir -p /tmp/screenmcp.iconset | |
| cp "$ICON_DIR/16.png" /tmp/screenmcp.iconset/icon_16x16.png | |
| cp "$ICON_DIR/32.png" /tmp/screenmcp.iconset/icon_16x16@2x.png | |
| cp "$ICON_DIR/32.png" /tmp/screenmcp.iconset/icon_32x32.png | |
| cp "$ICON_DIR/64.png" /tmp/screenmcp.iconset/icon_32x32@2x.png | |
| cp "$ICON_DIR/128.png" /tmp/screenmcp.iconset/icon_128x128.png | |
| cp "$ICON_DIR/256.png" /tmp/screenmcp.iconset/icon_128x128@2x.png | |
| cp "$ICON_DIR/256.png" /tmp/screenmcp.iconset/icon_256x256.png | |
| cp "$ICON_DIR/512.png" /tmp/screenmcp.iconset/icon_256x256@2x.png | |
| cp "$ICON_DIR/512.png" /tmp/screenmcp.iconset/icon_512x512.png | |
| cp "$ICON_DIR/1024.png" /tmp/screenmcp.iconset/icon_512x512@2x.png | |
| iconutil -c icns /tmp/screenmcp.iconset -o "${APP}/Contents/Resources/AppIcon.icns" | |
| rm -rf /tmp/screenmcp.iconset | |
| fi | |
| # Ad-hoc sign (no developer cert needed, satisfies basic integrity checks) | |
| codesign --force --deep -s - "${APP}" | |
| # Create .dmg using hdiutil (native on macOS runners) | |
| staging="${{ steps.archive.outputs.name }}" | |
| hdiutil create -volname "ScreenMCP" -srcfolder "${APP}" -ov -format UDZO "${staging}.dmg" | |
| echo "ASSET=${staging}.dmg" >> "$GITHUB_ENV" | |
| - name: Package (Unix, non-Mac) | |
| if: matrix.archive == 'tar.gz' && matrix.client != 'mac' | |
| shell: bash | |
| run: | | |
| staging="${{ steps.archive.outputs.name }}" | |
| mkdir -p "${staging}" | |
| cp "${{ matrix.client }}/target/${{ matrix.target }}/release/screenmcp-${{ matrix.client }}${{ matrix.ext }}" "${staging}/" | |
| cp "${{ matrix.client }}/README.md" "${staging}/" 2>/dev/null || true | |
| tar czf "${staging}.tar.gz" "${staging}" | |
| echo "ASSET=${staging}.tar.gz" >> "$GITHUB_ENV" | |
| - name: Package (Windows) | |
| if: matrix.archive == 'zip' | |
| shell: bash | |
| run: | | |
| staging="${{ steps.archive.outputs.name }}" | |
| mkdir -p "${staging}" | |
| cp "${{ matrix.client }}/target/${{ matrix.target }}/release/screenmcp-${{ matrix.client }}${{ matrix.ext }}" "${staging}/" | |
| cp "${{ matrix.client }}/README.md" "${staging}/" 2>/dev/null || true | |
| 7z a "${staging}.zip" "${staging}" | |
| echo "ASSET=${staging}.zip" >> "$GITHUB_ENV" | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ steps.archive.outputs.name }} | |
| path: ${{ env.ASSET }} | |
| build-android: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: android | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up JDK 17 | |
| uses: actions/setup-java@v4 | |
| with: | |
| java-version: '17' | |
| distribution: 'temurin' | |
| - name: Setup Android SDK | |
| uses: android-actions/setup-android@v3 | |
| - name: Verify google-services.json exists | |
| run: test -f app/google-services.json | |
| - name: Build release APK | |
| run: ./gradlew assembleRelease | |
| - name: Sign APK | |
| if: env.KEYSTORE_BASE64 != '' && env.KEYSTORE_PASSWORD != '' && env.KEY_ALIAS != '' && env.KEY_PASSWORD != '' | |
| env: | |
| KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} | |
| KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} | |
| KEY_ALIAS: ${{ secrets.KEY_ALIAS }} | |
| KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} | |
| run: | | |
| # Ensure build-tools are present (zipalign/apksigner) | |
| yes | sdkmanager --licenses >/dev/null || true | |
| sdkmanager "build-tools;35.0.0" | |
| echo "$KEYSTORE_BASE64" | base64 -d > keystore.jks | |
| APK=$(find app/build/outputs/apk/release -name '*.apk' | head -1) | |
| ALIGNED="app/build/outputs/apk/release/screenmcp-aligned.apk" | |
| SIGNED="app/build/outputs/apk/release/screenmcp-signed.apk" | |
| ZIPALIGN=$(find "$ANDROID_SDK_ROOT/build-tools" -name zipalign | sort -V | tail -1) | |
| APKSIGNER=$(find "$ANDROID_SDK_ROOT/build-tools" -name apksigner | sort -V | tail -1) | |
| "$ZIPALIGN" -v -p 4 "$APK" "$ALIGNED" | |
| "$APKSIGNER" sign \ | |
| --ks keystore.jks \ | |
| --ks-pass "pass:$KEYSTORE_PASSWORD" \ | |
| --ks-key-alias "$KEY_ALIAS" \ | |
| --key-pass "pass:$KEY_PASSWORD" \ | |
| --out "$SIGNED" \ | |
| "$ALIGNED" | |
| rm keystore.jks | |
| - name: Determine APK name | |
| id: apk | |
| shell: bash | |
| run: | | |
| version="${GITHUB_REF_NAME}" | |
| # Use signed APK if it exists, otherwise unsigned | |
| if [ -f "app/build/outputs/apk/release/screenmcp-signed.apk" ]; then | |
| APK_PATH="app/build/outputs/apk/release/screenmcp-signed.apk" | |
| else | |
| APK_PATH=$(find app/build/outputs/apk/release -name '*.apk' | head -1) | |
| fi | |
| DEST="screenmcp-android-${version}.apk" | |
| cp "$APK_PATH" "$DEST" | |
| echo "path=${DEST}" >> "$GITHUB_OUTPUT" | |
| echo "name=${DEST}" >> "$GITHUB_OUTPUT" | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: screenmcp-android | |
| path: android/${{ steps.apk.outputs.path }} | |
| build-windows-installer: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install build dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y nsis mingw-w64 | |
| - name: Install Rust toolchain (Windows GNU target) | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: x86_64-pc-windows-gnu | |
| - name: Build Windows installer (.exe) | |
| working-directory: windows | |
| run: ./build-installer.sh "${GITHUB_REF_NAME#v}" | |
| - name: Sign inner Windows binary before packaging | |
| if: env.CODE_SIGNING_PFX != '' | |
| env: | |
| CODE_SIGNING_PFX: ${{ secrets.CODE_SIGNING_PFX }} | |
| CODE_SIGNING_PASSWORD: ${{ secrets.CODE_SIGNING_PASSWORD }} | |
| run: | | |
| sudo apt-get install -y osslsigncode | |
| echo "$CODE_SIGNING_PFX" | base64 -d > codesign.pfx | |
| file="windows/target/installer/screenmcp-windows.exe" | |
| osslsigncode sign \ | |
| -pkcs12 codesign.pfx \ | |
| -pass "$CODE_SIGNING_PASSWORD" \ | |
| -ts http://timestamp.digicert.com \ | |
| -h sha256 \ | |
| -in "$file" \ | |
| -out "${file}.signed" | |
| mv "${file}.signed" "$file" | |
| rm codesign.pfx | |
| - name: Rebuild installer with signed binary | |
| if: env.CODE_SIGNING_PFX != '' | |
| env: | |
| CODE_SIGNING_PFX: ${{ secrets.CODE_SIGNING_PFX }} | |
| working-directory: windows/target/installer | |
| run: | | |
| version="${GITHUB_REF_NAME#v}" | |
| sed -i "s/^!define APP_VERSION.*$/!define APP_VERSION \"${version}\"/" installer.nsi | |
| makensis installer.nsi | |
| - name: Sign Windows installer | |
| if: env.CODE_SIGNING_PFX != '' | |
| env: | |
| CODE_SIGNING_PFX: ${{ secrets.CODE_SIGNING_PFX }} | |
| CODE_SIGNING_PASSWORD: ${{ secrets.CODE_SIGNING_PASSWORD }} | |
| run: | | |
| which osslsigncode || sudo apt-get install -y osslsigncode | |
| echo "$CODE_SIGNING_PFX" | base64 -d > codesign.pfx | |
| version="${GITHUB_REF_NAME#v}" | |
| file="windows/target/installer/screenmcp-setup-${version}-x86_64.exe" | |
| osslsigncode sign \ | |
| -pkcs12 codesign.pfx \ | |
| -pass "$CODE_SIGNING_PASSWORD" \ | |
| -ts http://timestamp.digicert.com \ | |
| -h sha256 \ | |
| -in "$file" \ | |
| -out "${file}.signed" | |
| mv "${file}.signed" "$file" | |
| rm codesign.pfx | |
| - name: Determine installer name | |
| id: installer | |
| shell: bash | |
| run: | | |
| version="${GITHUB_REF_NAME#v}" | |
| file="screenmcp-setup-${version}-x86_64.exe" | |
| cp "windows/target/installer/${file}" "${file}" | |
| echo "path=${file}" >> "$GITHUB_OUTPUT" | |
| echo "name=${file}" >> "$GITHUB_OUTPUT" | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: screenmcp-windows-installer | |
| path: ${{ steps.installer.outputs.path }} | |
| build-linux-deb: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libxdo-dev \ | |
| libx11-dev \ | |
| libxrandr-dev \ | |
| libxi-dev \ | |
| libxtst-dev \ | |
| libglib2.0-dev \ | |
| libgtk-3-dev \ | |
| libayatana-appindicator3-dev \ | |
| libxcb-shape0-dev \ | |
| libxcb-xfixes0-dev \ | |
| libdbus-1-dev \ | |
| libasound2-dev | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Build | |
| working-directory: linux | |
| run: cargo build --release | |
| - name: Package .deb | |
| shell: bash | |
| run: | | |
| version="${GITHUB_REF_NAME#v}" | |
| arch="amd64" | |
| pkg="screenmcp_${version}_${arch}" | |
| binary="linux/target/release/screenmcp-linux" | |
| strip "$binary" 2>/dev/null || true | |
| mkdir -p "${pkg}/DEBIAN" | |
| mkdir -p "${pkg}/usr/bin" | |
| mkdir -p "${pkg}/usr/share/applications" | |
| mkdir -p "${pkg}/usr/share/icons/hicolor/512x512/apps" | |
| cp "$binary" "${pkg}/usr/bin/screenmcp-linux" | |
| chmod 755 "${pkg}/usr/bin/screenmcp-linux" | |
| cp linux/screenmcp.desktop "${pkg}/usr/share/applications/" | |
| cp linux/assets/icon-app.png "${pkg}/usr/share/icons/hicolor/512x512/apps/screenmcp.png" | |
| installed_size=$(du -sk "${pkg}" | cut -f1) | |
| cat > "${pkg}/DEBIAN/control" <<EOF | |
| Package: screenmcp | |
| Version: ${version} | |
| Section: utils | |
| Priority: optional | |
| Architecture: ${arch} | |
| Installed-Size: ${installed_size} | |
| Depends: libgtk-3-0, libssl3 | libssl1.1, libxdo3, wmctrl, libasound2 | |
| Maintainer: ScreenMCP <support@screenmcp.com> | |
| Homepage: https://screenmcp.com | |
| Description: AI desktop control via MCP | |
| ScreenMCP gives AI assistants real-time vision and control | |
| over desktop computers via the Model Context Protocol. | |
| System tray app that connects to a ScreenMCP worker. | |
| EOF | |
| # Remove leading whitespace from control file (YAML indent) | |
| sed -i 's/^ //' "${pkg}/DEBIAN/control" | |
| cat > "${pkg}/DEBIAN/postinst" <<'SCRIPT' | |
| #!/bin/sh | |
| set -e | |
| gtk-update-icon-cache -f -t /usr/share/icons/hicolor 2>/dev/null || true | |
| update-desktop-database /usr/share/applications 2>/dev/null || true | |
| SCRIPT | |
| sed -i 's/^ //' "${pkg}/DEBIAN/postinst" | |
| chmod 755 "${pkg}/DEBIAN/postinst" | |
| cat > "${pkg}/DEBIAN/postrm" <<'SCRIPT' | |
| #!/bin/sh | |
| set -e | |
| gtk-update-icon-cache -f -t /usr/share/icons/hicolor 2>/dev/null || true | |
| update-desktop-database /usr/share/applications 2>/dev/null || true | |
| SCRIPT | |
| sed -i 's/^ //' "${pkg}/DEBIAN/postrm" | |
| chmod 755 "${pkg}/DEBIAN/postrm" | |
| dpkg-deb --build --root-owner-group "${pkg}" "screenmcp-linux-${version}-amd64.deb" | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: screenmcp-linux-deb | |
| path: screenmcp-linux-*.deb | |
| release: | |
| needs: [build-desktop, build-android, build-windows-installer, build-linux-deb] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Collect release assets | |
| run: | | |
| mkdir -p release | |
| find artifacts -type f \( -name '*.tar.gz' -o -name '*.zip' -o -name '*.apk' -o -name '*.dmg' -o -name '*.deb' -o -name 'screenmcp-setup-*-x86_64.exe' \) -exec mv {} release/ \; | |
| ls -lh release/ | |
| - name: Generate checksums | |
| working-directory: release | |
| run: | | |
| sha256sum * > SHA256SUMS.txt | |
| cat SHA256SUMS.txt | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: | | |
| release/* | |
| generate_release_notes: true | |
| draft: false | |
| prerelease: ${{ contains(github.ref_name, '-') }} | |
| body: | | |
| ## Downloads | |
| | Platform | File | Notes | | |
| |----------|------|-------| | |
| | **macOS (Apple Silicon)** | `screenmcp-mac-aarch64-apple-darwin-*.dmg` | M1/M2/M3/M4 Macs | | |
| | **macOS (Intel)** | `screenmcp-mac-x86_64-apple-darwin-*.dmg` | Older Intel Macs | | |
| | **Windows** | `screenmcp-windows-*.zip` | x86_64 | | |
| | **Windows Installer** | `screenmcp-setup-*-x86_64.exe` | NSIS setup wizard | | |
| | **Linux (.deb)** | `screenmcp-linux-*-amd64.deb` | Debian/Ubuntu x86_64 | | |
| | **Linux (.tar.gz)** | `screenmcp-linux-*.tar.gz` | x86_64 | | |
| | **Android** | `screenmcp-android-*.apk` | ARM/x86 | | |
| ## macOS Installation | |
| 1. Download the `.dmg` for your architecture (Apple Silicon or Intel) | |
| 2. Open the `.dmg` and drag **ScreenMCP** to `/Applications` | |
| 3. **Important** — the app is not notarized, so macOS Gatekeeper will block it. Run: | |
| ```bash | |
| xattr -cr /Applications/ScreenMCP.app | |
| ``` | |
| 4. Launch ScreenMCP — it appears as a menu bar icon (no Dock icon) | |
| 5. Grant **Accessibility** and **Screen Recording** permissions when prompted | |
| ## Windows | |
| Extract the archive and run `screenmcp-windows.exe`, or use the NSIS installer. | |
| > **Browser may block the download** — click the small arrow (⌄) next to the download and choose **"Keep"**. See [visual guide](https://github.com/shimondoodkin/screenmcp/blob/main/docs/on-windows-how-to-download-new-setup-file.png). | |
| ## Linux | |
| Install the `.deb` package: | |
| ```bash | |
| sudo dpkg -i screenmcp-linux-*-amd64.deb | |
| ``` | |
| Or extract the `.tar.gz` and run the `screenmcp-linux` binary directly. | |
| ## Android | |
| Install the APK directly. You may need to enable "Install from unknown sources". |