diff --git a/.github/workflows/desktop-macos-release.yml b/.github/workflows/desktop-macos-release.yml deleted file mode 100644 index 31073cb4..00000000 --- a/.github/workflows/desktop-macos-release.yml +++ /dev/null @@ -1,151 +0,0 @@ -name: Desktop macOS Release - -on: - workflow_dispatch: - inputs: - tag: - description: 'Release tag to create or update (defaults to v)' - required: false - type: string - release_name: - description: 'Release name (defaults to "CloudCLI Desktop macOS ")' - required: false - type: string - prerelease: - description: 'Mark the GitHub release as a prerelease' - required: true - default: false - type: boolean - -jobs: - build-macos: - name: Build signed macOS desktop app - runs-on: macos-latest - permissions: - contents: write - steps: - - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Set up Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 - with: - node-version: 22 - cache: npm - - - name: Install dependencies - run: npm ci - - - name: Typecheck - run: npm run typecheck - - - name: Resolve release metadata - id: release - env: - TAG_INPUT: ${{ inputs.tag }} - RELEASE_NAME_INPUT: ${{ inputs.release_name }} - run: | - VERSION="$(node -p "require('./package.json').version")" - TAG="$TAG_INPUT" - if [ -z "$TAG" ]; then - TAG="v${VERSION}" - fi - TAG="$(printf '%s' "$TAG" | tr -d '\r\n' | sed 's/[^A-Za-z0-9._-]/-/g')" - if [ -z "$TAG" ]; then - echo "Resolved release tag is empty after normalization." >&2 - exit 1 - fi - - RELEASE_NAME="$RELEASE_NAME_INPUT" - if [ -z "$RELEASE_NAME" ]; then - RELEASE_NAME="CloudCLI Desktop macOS ${TAG}" - fi - RELEASE_NAME_DELIMITER="release_name_$(uuidgen)" - - { - echo "tag=$TAG" - echo "release_name<<$RELEASE_NAME_DELIMITER" - printf '%s\n' "$RELEASE_NAME" - echo "$RELEASE_NAME_DELIMITER" - echo "server_bundle_tag=cloudcli-local-server-${TAG}" - } >> "$GITHUB_OUTPUT" - - - name: Configure release server bundle source - env: - SERVER_BUNDLE_TAG: ${{ steps.release.outputs.server_bundle_tag }} - run: printf '{"releaseTag":"%s"}\n' "$SERVER_BUNDLE_TAG" > electron/server-bundle-config.json - - - name: Verify signing secrets are configured - run: | - test -n "$CSC_LINK" - test -n "$CSC_KEY_PASSWORD" - test -n "$APPLE_ID" - test -n "$APPLE_APP_SPECIFIC_PASSWORD" - test -n "$APPLE_TEAM_ID" - env: - CSC_LINK: ${{ secrets.CSC_LINK }} - CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} - APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} - APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - - - name: Build signed and notarized macOS artifacts - run: npm run desktop:dist:mac -- --publish never - env: - CLOUDCLI_SEMANTICS_BUILD_REQUIRED: "1" - CSC_LINK: ${{ secrets.CSC_LINK }} - CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} - APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} - APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - - - name: Build local server bundle - run: node scripts/release/build-server-bundle.js - - - name: Verify local server runtime artifacts - run: | - test -n "$(find release/local-server -maxdepth 1 -name 'cloudcli-local-server-*.tar.gz' -print -quit)" - test -n "$(find release/local-server -maxdepth 1 -name 'cloudcli-local-server-*.tar.gz.sha256' -print -quit)" - - - name: Publish local server runtime assets - uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 - with: - tag_name: ${{ steps.release.outputs.server_bundle_tag }} - target_commitish: ${{ github.sha }} - name: CloudCLI Local Server Runtime (${{ steps.release.outputs.tag }}) - body: | - This prerelease contains the Local mode runtime for CloudCLI Desktop. - - Download CloudCLI Desktop from the main ${{ steps.release.outputs.tag }} release. When you open Local CloudCLI, the desktop app automatically downloads the matching runtime from this prerelease. - - You do not need to download these runtime files manually. - prerelease: true - fail_on_unmatched_files: false - overwrite_files: true - files: | - release/local-server/* - - - name: Verify macOS artifacts - run: | - test -n "$(find release/desktop -maxdepth 1 -name '*.dmg' -print -quit)" - shasum -a 256 release/desktop/*.dmg > release/SHASUMS256.txt - cat release/SHASUMS256.txt - - - name: Publish GitHub release assets - uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 - with: - tag_name: ${{ steps.release.outputs.tag }} - target_commitish: ${{ github.sha }} - name: ${{ steps.release.outputs.release_name }} - body: | - Download the CloudCLI Desktop installer for your Mac. - - The local server runtime used by local mode is installed automatically by the desktop app. You do not need to download any server bundle manually. - prerelease: ${{ inputs.prerelease }} - fail_on_unmatched_files: false - files: | - release/desktop/*.dmg - release/SHASUMS256.txt diff --git a/.github/workflows/desktop-release.yml b/.github/workflows/desktop-release.yml new file mode 100644 index 00000000..43e5fda9 --- /dev/null +++ b/.github/workflows/desktop-release.yml @@ -0,0 +1,305 @@ +name: Desktop Release + +on: + workflow_dispatch: + inputs: + tag: + description: "Release tag to create or update (defaults to v)" + required: false + type: string + release_name: + description: 'Release name (defaults to "CloudCLI Desktop ")' + required: false + type: string + prerelease: + description: "Mark the GitHub release as a prerelease" + required: true + default: false + type: boolean + +jobs: + resolve-release: + name: Resolve release metadata + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + tag: ${{ steps.release.outputs.tag }} + release_name: ${{ steps.release.outputs.release_name }} + server_bundle_tag: ${{ steps.release.outputs.server_bundle_tag }} + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + persist-credentials: false + + - name: Resolve release metadata + id: release + env: + TAG_INPUT: ${{ inputs.tag }} + RELEASE_NAME_INPUT: ${{ inputs.release_name }} + run: | + VERSION="$(node -p "require('./package.json').version")" + TAG="$TAG_INPUT" + if [ -z "$TAG" ]; then + TAG="v${VERSION}" + fi + TAG="$(printf '%s' "$TAG" | tr -d '\r\n' | sed 's/[^A-Za-z0-9._-]/-/g')" + if [ -z "$TAG" ]; then + echo "Resolved release tag is empty after normalization." >&2 + exit 1 + fi + + RELEASE_NAME="$RELEASE_NAME_INPUT" + if [ -z "$RELEASE_NAME" ]; then + RELEASE_NAME="CloudCLI Desktop ${TAG}" + fi + RELEASE_NAME_DELIMITER="release_name_${GITHUB_RUN_ID}_${GITHUB_RUN_ATTEMPT}" + + { + echo "tag=$TAG" + echo "release_name<<$RELEASE_NAME_DELIMITER" + printf '%s\n' "$RELEASE_NAME" + echo "$RELEASE_NAME_DELIMITER" + echo "server_bundle_tag=cloudcli-local-server-${TAG}" + } >> "$GITHUB_OUTPUT" + + build-macos: + name: Build signed macOS desktop app + needs: resolve-release + runs-on: macos-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Typecheck + run: npm run typecheck + + - name: Configure release server bundle source + env: + SERVER_BUNDLE_TAG: ${{ needs.resolve-release.outputs.server_bundle_tag }} + run: printf '{"releaseTag":"%s"}\n' "$SERVER_BUNDLE_TAG" > electron/server-bundle-config.json + + - name: Verify macOS signing secrets are configured + run: | + test -n "$CSC_LINK" + test -n "$CSC_KEY_PASSWORD" + test -n "$APPLE_ID" + test -n "$APPLE_APP_SPECIFIC_PASSWORD" + test -n "$APPLE_TEAM_ID" + env: + CSC_LINK: ${{ secrets.CSC_LINK }} + CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + + - name: Build signed and notarized macOS artifacts + run: npm run desktop:dist:mac -- --publish never + env: + CLOUDCLI_SEMANTICS_BUILD_REQUIRED: "1" + CSC_LINK: ${{ secrets.CSC_LINK }} + CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + + - name: Build macOS local server bundle + run: node scripts/release/build-server-bundle.js + + - name: Stage macOS release assets + run: | + mkdir -p desktop-release-assets server-release-assets + test -n "$(find release/desktop -maxdepth 1 -name '*.dmg' -print -quit)" + shasum -a 256 release/desktop/*.dmg > desktop-release-assets/SHASUMS256-macos.txt + cp release/desktop/*.dmg desktop-release-assets/ + + test -n "$(find release/local-server -maxdepth 1 -name 'cloudcli-local-server-*.tar.gz' -print -quit)" + test -n "$(find release/local-server -maxdepth 1 -name 'cloudcli-local-server-*.tar.gz.sha256' -print -quit)" + cp release/local-server/* server-release-assets/ + + - name: Upload macOS desktop assets + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: desktop-release-macos + path: desktop-release-assets/* + if-no-files-found: error + + - name: Upload macOS server assets + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: server-release-macos + path: server-release-assets/* + if-no-files-found: error + + build-windows: + name: Build Windows desktop app + needs: resolve-release + runs-on: windows-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + env: + GITHUB_TOKEN: ${{ github.token }} + + - name: Typecheck + run: npm run typecheck + + - name: Configure release server bundle source + shell: bash + env: + SERVER_BUNDLE_TAG: ${{ needs.resolve-release.outputs.server_bundle_tag }} + run: printf '{"releaseTag":"%s"}\n' "$SERVER_BUNDLE_TAG" > electron/server-bundle-config.json + + - name: Check Windows signing secrets + id: windows-signing + shell: bash + env: + WINDOWS_CSC_LINK: ${{ secrets.WINDOWS_CSC_LINK }} + WINDOWS_CSC_KEY_PASSWORD: ${{ secrets.WINDOWS_CSC_KEY_PASSWORD }} + run: | + if [ -n "$WINDOWS_CSC_LINK" ] && [ -n "$WINDOWS_CSC_KEY_PASSWORD" ]; then + echo "enabled=true" >> "$GITHUB_OUTPUT" + else + echo "enabled=false" >> "$GITHUB_OUTPUT" + fi + + - name: Build signed Windows artifacts + if: steps.windows-signing.outputs.enabled == 'true' + run: npm run desktop:dist:win -- --publish never + env: + CLOUDCLI_SEMANTICS_BUILD_REQUIRED: "1" + CSC_LINK: ${{ secrets.WINDOWS_CSC_LINK }} + CSC_KEY_PASSWORD: ${{ secrets.WINDOWS_CSC_KEY_PASSWORD }} + + - name: Build unsigned Windows artifacts + if: steps.windows-signing.outputs.enabled != 'true' + run: npm run desktop:dist:win -- --publish never + env: + CLOUDCLI_SEMANTICS_BUILD_REQUIRED: "1" + CSC_IDENTITY_AUTO_DISCOVERY: "false" + + - name: Build Windows local server bundle + run: node scripts/release/build-server-bundle.js + + - name: Stage Windows release assets + shell: bash + run: | + mkdir -p desktop-release-assets server-release-assets + test -n "$(find release/desktop -maxdepth 1 -name '*.exe' -print -quit)" + sha256sum release/desktop/*.exe > desktop-release-assets/SHASUMS256-windows.txt + cp release/desktop/*.exe desktop-release-assets/ + + test -n "$(find release/local-server -maxdepth 1 -name 'cloudcli-local-server-*.tar.gz' -print -quit)" + test -n "$(find release/local-server -maxdepth 1 -name 'cloudcli-local-server-*.tar.gz.sha256' -print -quit)" + cp release/local-server/* server-release-assets/ + + - name: Upload Windows desktop assets + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: desktop-release-windows + path: desktop-release-assets/* + if-no-files-found: error + + - name: Upload Windows server assets + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: server-release-windows + path: server-release-assets/* + if-no-files-found: error + + publish: + name: Publish desktop release + needs: + - resolve-release + - build-macos + - build-windows + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Download desktop assets + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6 + with: + pattern: desktop-release-* + path: release/desktop + merge-multiple: true + + - name: Download server assets + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6 + with: + pattern: server-release-* + path: release/local-server + merge-multiple: true + + - name: Verify release assets + run: | + test -n "$(find release/desktop -maxdepth 1 -name '*.dmg' -print -quit)" + test -n "$(find release/desktop -maxdepth 1 -name '*.exe' -print -quit)" + test -f release/desktop/SHASUMS256-macos.txt + test -f release/desktop/SHASUMS256-windows.txt + test -n "$(find release/local-server -maxdepth 1 -name 'cloudcli-local-server-*.tar.gz' -print -quit)" + test -n "$(find release/local-server -maxdepth 1 -name 'cloudcli-local-server-*.tar.gz.sha256' -print -quit)" + find release -maxdepth 2 -type f -print | sort + + - name: Publish local server runtime assets + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 + with: + tag_name: ${{ needs.resolve-release.outputs.server_bundle_tag }} + target_commitish: ${{ github.sha }} + name: CloudCLI Local Server Runtime (${{ needs.resolve-release.outputs.tag }}) + body: | + This prerelease contains the Local mode runtime for CloudCLI Desktop. + + Download CloudCLI Desktop from the main ${{ needs.resolve-release.outputs.tag }} release. When you open Local CloudCLI, the desktop app automatically downloads the matching runtime from this prerelease. + + You do not need to download these runtime files manually. + prerelease: true + fail_on_unmatched_files: false + overwrite_files: true + files: | + release/local-server/* + + - name: Publish GitHub release assets + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 + with: + tag_name: ${{ needs.resolve-release.outputs.tag }} + target_commitish: ${{ github.sha }} + name: ${{ needs.resolve-release.outputs.release_name }} + body: | + Download the CloudCLI Desktop installer for your platform. + + The local server runtime used by local mode is installed automatically by the desktop app. You do not need to download any server bundle manually. + prerelease: ${{ inputs.prerelease }} + fail_on_unmatched_files: false + overwrite_files: true + files: | + release/desktop/* diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d33fd337..a1253ca9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,9 +4,9 @@ on: workflow_dispatch: inputs: increment: - description: 'Version bump: patch, minor, major, or explicit (e.g. 1.27.0)' + description: "Version bump: patch, minor, major, or explicit (e.g. 1.27.0)" required: true - default: 'patch' + default: "patch" type: string release_name: description: 'Custom release name (optional, defaults to "CloudCLI UI vX.Y.Z")' @@ -124,6 +124,9 @@ jobs: path: server/modules/computer-use/semantics/bin merge-multiple: true + - name: Restore semantic helper permissions + run: find server/modules/computer-use/semantics/bin -path '*/darwin-*/CloudCLISemantics' -type f -exec chmod 755 {} + + - name: Verify bundled semantic helpers run: | test -x server/modules/computer-use/semantics/bin/darwin-arm64/CloudCLISemantics