native-devtools-mcp 0.10.1

MCP server for computer use & browser automation — screenshot, OCR, click, type, find_text, Chrome/Electron CDP, template matching. macOS, Windows & Android.
name: Release

on:
  push:
    tags:
      - "v*"

permissions:
  contents: write
  id-token: write

env:
  # Opt every Node 20 action into Node 24 ahead of the June 2026 default flip
  # and the September 2026 Node 20 removal.
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

jobs:
  build:
    name: Build - ${{ matrix.target }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        include:
          - target: aarch64-apple-darwin
            os: macos-14
            npm_dir: darwin-arm64
          - target: x86_64-pc-windows-msvc
            os: windows-latest
            npm_dir: win32-x64

    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - name: Build release binary
        run: cargo build --release --target ${{ matrix.target }}

      - name: Package binary for GitHub Release (Unix)
        if: runner.os != 'Windows'
        run: |
          cd target/${{ matrix.target }}/release
          tar -czvf ../../../native-devtools-mcp-${{ matrix.target }}.tar.gz native-devtools-mcp

      - name: Package binary for GitHub Release (Windows)
        if: runner.os == 'Windows'
        run: |
          cd target/${{ matrix.target }}/release
          Compress-Archive -Path native-devtools-mcp.exe -DestinationPath ../../../native-devtools-mcp-${{ matrix.target }}.zip
        shell: pwsh

      - name: Prepare npm package (Unix)
        if: runner.os != 'Windows'
        run: |
          mkdir -p npm/${{ matrix.npm_dir }}/bin
          cp target/${{ matrix.target }}/release/native-devtools-mcp npm/${{ matrix.npm_dir }}/bin/
          chmod +x npm/${{ matrix.npm_dir }}/bin/native-devtools-mcp

      - name: Prepare npm package (Windows)
        if: runner.os == 'Windows'
        run: |
          New-Item -ItemType Directory -Force -Path npm/${{ matrix.npm_dir }}/bin
          Copy-Item target/${{ matrix.target }}/release/native-devtools-mcp.exe npm/${{ matrix.npm_dir }}/bin/
        shell: pwsh

      - name: Upload GitHub Release artifact (Unix)
        if: runner.os != 'Windows'
        uses: actions/upload-artifact@v4
        with:
          name: native-devtools-mcp-${{ matrix.target }}
          path: native-devtools-mcp-${{ matrix.target }}.tar.gz

      - name: Upload GitHub Release artifact (Windows)
        if: runner.os == 'Windows'
        uses: actions/upload-artifact@v4
        with:
          name: native-devtools-mcp-${{ matrix.target }}
          path: native-devtools-mcp-${{ matrix.target }}.zip

      - name: Upload npm package artifact
        uses: actions/upload-artifact@v4
        with:
          name: npm-${{ matrix.npm_dir }}
          path: npm/${{ matrix.npm_dir }}

  build-macos-app:
    name: Build macOS App (Signed + Notarized)
    runs-on: macos-14
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: aarch64-apple-darwin

      - name: Import signing certificate
        env:
          MACOS_SIGN_CERT_BASE64: ${{ secrets.MACOS_SIGN_CERT_BASE64 }}
          MACOS_SIGN_CERT_PASSWORD: ${{ secrets.MACOS_SIGN_CERT_PASSWORD }}
        run: |
          # Decode certificate
          echo "$MACOS_SIGN_CERT_BASE64" | base64 --decode > signing.p12

          # Create temporary keychain
          security create-keychain -p "temppass" build.keychain
          security set-keychain-settings -lut 21600 build.keychain
          security unlock-keychain -p "temppass" build.keychain

          # Import certificate
          security import signing.p12 -k build.keychain -P "$MACOS_SIGN_CERT_PASSWORD" -T /usr/bin/codesign

          # Set key partition list (required for codesign to access the key)
          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "temppass" build.keychain

          # Add to search list
          security list-keychains -d user -s build.keychain login.keychain

          # Clean up
          rm signing.p12

      - name: Get version from tag
        id: version
        run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

      - name: Build macOS app bundle
        env:
          TARGET: aarch64-apple-darwin
          VERSION: ${{ steps.version.outputs.VERSION }}
          SIGN_IDENTITY: ${{ secrets.MACOS_SIGN_IDENTITY }}
        run: ./scripts/build-macos-app.sh

      - name: Notarize and create DMG
        env:
          SIGN_IDENTITY: ${{ secrets.MACOS_SIGN_IDENTITY }}
          NOTARY_API_KEY_ID: ${{ secrets.APPSTORE_API_KEY_ID }}
          NOTARY_API_ISSUER: ${{ secrets.APPSTORE_API_ISSUER_ID }}
          APPSTORE_API_PRIVATE_KEY: ${{ secrets.APPSTORE_API_PRIVATE_KEY }}
          VERSION: ${{ steps.version.outputs.VERSION }}
        run: |
          # Write API key to file
          echo "$APPSTORE_API_PRIVATE_KEY" > AuthKey.p8

          # Run notarization script
          NOTARY_API_KEY=AuthKey.p8 ./scripts/notarize-macos-app.sh

          # Clean up
          rm AuthKey.p8

      - name: Upload DMG artifact
        uses: actions/upload-artifact@v4
        with:
          name: macos-app-dmg
          path: dist/*.dmg

  release:
    name: Create GitHub Release
    needs: [build, build-macos-app]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Download all artifacts
        uses: actions/download-artifact@v4
        with:
          path: artifacts

      - name: Generate checksums
        run: |
          cd artifacts

          # Hash the raw binaries from the npm artifacts
          sha256sum npm-darwin-arm64/bin/native-devtools-mcp > ../checksums.txt
          sed -i 's|npm-darwin-arm64/bin/native-devtools-mcp|native-devtools-mcp (aarch64-apple-darwin)|' ../checksums.txt

          sha256sum npm-win32-x64/bin/native-devtools-mcp.exe >> ../checksums.txt
          sed -i 's|npm-win32-x64/bin/native-devtools-mcp.exe|native-devtools-mcp.exe (x86_64-pc-windows-msvc)|' ../checksums.txt

          # Hash the archive files
          sha256sum native-devtools-mcp-aarch64-apple-darwin/*.tar.gz >> ../checksums.txt
          sed -i 's|native-devtools-mcp-aarch64-apple-darwin/||' ../checksums.txt

          sha256sum native-devtools-mcp-x86_64-pc-windows-msvc/*.zip >> ../checksums.txt
          sed -i 's|native-devtools-mcp-x86_64-pc-windows-msvc/||' ../checksums.txt

          # Hash the DMG if present
          if ls macos-app-dmg/*.dmg 1>/dev/null 2>&1; then
            sha256sum macos-app-dmg/*.dmg >> ../checksums.txt
            sed -i 's|macos-app-dmg/||' ../checksums.txt
          fi

          cd ..
          echo "Generated checksums:"
          cat checksums.txt

      - name: Extract changelog for this version
        id: changelog
        run: |
          VERSION="${GITHUB_REF#refs/tags/v}"
          # Extract the section between "## vX.Y.Z" and the next "## v" heading (or EOF)
          NOTES=$(awk "/^## v${VERSION}\$/ { found=1; next } found && /^## v/ { exit } found { print }" CHANGELOG.md | sed '/^$/N;/^\n$/d')
          if [ -n "$NOTES" ]; then
            echo "has_notes=true" >> "$GITHUB_OUTPUT"
            # Write to file for body_path (handles multiline safely)
            echo "$NOTES" > RELEASE_BODY.md
          else
            echo "has_notes=false" >> "$GITHUB_OUTPUT"
          fi

      - name: Create GitHub Release (with changelog)
        if: steps.changelog.outputs.has_notes == 'true'
        uses: softprops/action-gh-release@v1
        with:
          files: |
            artifacts/native-devtools-mcp-*/*.tar.gz
            artifacts/native-devtools-mcp-*/*.zip
            artifacts/macos-app-dmg/*.dmg
            checksums.txt
          body_path: RELEASE_BODY.md

      - name: Create GitHub Release (auto-generated)
        if: steps.changelog.outputs.has_notes == 'false'
        uses: softprops/action-gh-release@v1
        with:
          files: |
            artifacts/native-devtools-mcp-*/*.tar.gz
            artifacts/native-devtools-mcp-*/*.zip
            artifacts/macos-app-dmg/*.dmg
            checksums.txt
          generate_release_notes: true

  publish-npm:
    name: Publish to npm
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v4

      - name: Download npm artifacts
        uses: actions/download-artifact@v4
        with:
          path: artifacts

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          # Node 24 for npm v11, which supports the current OIDC handshake
          # required by Trusted Publishing. Node 22 (npm v10) gets silently
          # treated as anonymous and publish fails with a misleading 404.
          node-version: "24"
          registry-url: "https://registry.npmjs.org"

      - name: Copy binaries to npm packages
        run: |
          # Copy macOS binary
          mkdir -p npm/darwin-arm64/bin
          cp artifacts/npm-darwin-arm64/bin/native-devtools-mcp npm/darwin-arm64/bin/
          chmod +x npm/darwin-arm64/bin/native-devtools-mcp

          # Copy Windows binary
          mkdir -p npm/win32-x64/bin
          cp artifacts/npm-win32-x64/bin/native-devtools-mcp.exe npm/win32-x64/bin/

      - name: Copy README to npm package
        run: cp README.md npm/

      - name: Update versions from tag
        run: |
          VERSION=${GITHUB_REF#refs/tags/v}
          echo "Publishing version: $VERSION"

          # Update main package version
          cd npm
          npm version $VERSION --no-git-tag-version --allow-same-version

          # Update platform package versions
          cd darwin-arm64
          npm version $VERSION --no-git-tag-version --allow-same-version
          cd ../win32-x64
          npm version $VERSION --no-git-tag-version --allow-same-version
          cd ..

          # Update optionalDependencies versions in main package
          sed -i "s/\"@sh3ll3x3c\/native-devtools-mcp-darwin-arm64\": \".*\"/\"@sh3ll3x3c\/native-devtools-mcp-darwin-arm64\": \"$VERSION\"/" package.json
          sed -i "s/\"@sh3ll3x3c\/native-devtools-mcp-win32-x64\": \".*\"/\"@sh3ll3x3c\/native-devtools-mcp-win32-x64\": \"$VERSION\"/" package.json

      - name: Publish darwin-arm64 package
        run: npm publish --provenance --access public
        working-directory: npm/darwin-arm64

      - name: Publish win32-x64 package
        run: npm publish --provenance --access public
        working-directory: npm/win32-x64

      - name: Wait for npm registry to update
        run: sleep 30

      - name: Publish main package
        run: npm publish --provenance --access public
        working-directory: npm

  publish-mcp-registry:
    name: Publish to MCP Registry
    needs: publish-npm
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v4

      - name: Install mcp-publisher
        run: |
          curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz" | tar xz mcp-publisher
          sudo mv mcp-publisher /usr/local/bin/

      - name: Authenticate to MCP Registry
        run: mcp-publisher login github-oidc

      - name: Update version in server.json
        run: |
          VERSION=${GITHUB_REF#refs/tags/v}
          jq --arg v "$VERSION" '.version = $v | .packages[0].version = $v' server.json > server.tmp && mv server.tmp server.json

      - name: Publish to MCP Registry
        run: mcp-publisher publish

  publish-crates:
    name: Publish to crates.io
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: read
      # OIDC Trusted Publishing — crates.io mints a short-lived token per run,
      # so no CARGO_REGISTRY_TOKEN secret is stored anywhere. Requires a Trusted
      # Publisher configured on crates.io for this repo + workflow (release.yml).
      id-token: write
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Authenticate to crates.io (Trusted Publishing)
        uses: rust-lang/crates-io-auth-action@v1
        id: auth

      - name: Publish to crates.io
        # Version comes from Cargo.toml at the tagged commit (bumped in the
        # release commit), so it always matches the tag being released.
        run: cargo publish
        env:
          CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}