Merge pull request #28 from fancyboi999/codex/fix-language-enforcement #29
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: Build and Release | |
| on: | |
| push: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| inputs: | |
| platform: | |
| description: "Platform to build" | |
| required: true | |
| default: "all" | |
| type: choice | |
| options: | |
| - all | |
| - linux | |
| - windows | |
| - macos-intel | |
| - macos-arm64 | |
| release: | |
| description: "Create a release after build" | |
| required: false | |
| default: false | |
| type: boolean | |
| env: | |
| CARGO_INCREMENTAL: 0 | |
| RUST_BACKTRACE: short | |
| jobs: | |
| build: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: linux | |
| os: ubuntu-22.04 | |
| rust_target: x86_64-unknown-linux-gnu | |
| tauri_target: x86_64-unknown-linux-gnu | |
| bun_target: bun-linux-x64 | |
| name: linux-x86_64 | |
| - platform: windows | |
| os: windows-latest | |
| rust_target: x86_64-pc-windows-msvc | |
| tauri_target: x86_64-pc-windows-msvc | |
| bun_target: bun-windows-x64 | |
| name: windows-x86_64 | |
| - platform: macos-intel | |
| os: macos-latest | |
| rust_target: x86_64-apple-darwin | |
| tauri_target: x86_64-apple-darwin | |
| bun_target: bun-darwin-x64 | |
| name: macos-intel | |
| - platform: macos-arm64 | |
| os: macos-latest | |
| rust_target: aarch64-apple-darwin | |
| tauri_target: aarch64-apple-darwin | |
| bun_target: bun-darwin-arm64 | |
| name: macos-arm64 | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Check platform filter | |
| id: check | |
| shell: bash | |
| run: | | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.platform }}" != "all" && "${{ github.event.inputs.platform }}" != "${{ matrix.platform }}" ]]; then | |
| echo "skip=true" >> $GITHUB_OUTPUT | |
| echo "Skipping ${{ matrix.platform }} (selected: ${{ github.event.inputs.platform }})" | |
| else | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| echo "Building ${{ matrix.platform }}" | |
| fi | |
| - name: Checkout repository | |
| if: steps.check.outputs.skip != 'true' | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies (Linux) | |
| if: steps.check.outputs.skip != 'true' && matrix.platform == 'linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libfuse2 file | |
| # Fix for linuxdeploy in GitHub Actions (FUSE not available) | |
| echo "APPIMAGE_EXTRACT_AND_RUN=1" >> $GITHUB_ENV | |
| - name: Setup Node.js | |
| if: steps.check.outputs.skip != 'true' | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| - name: Setup pnpm | |
| if: steps.check.outputs.skip != 'true' | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9 | |
| - name: Setup Bun | |
| if: steps.check.outputs.skip != 'true' | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Get pnpm store directory | |
| if: steps.check.outputs.skip != 'true' | |
| shell: bash | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV | |
| - name: Setup pnpm cache | |
| if: steps.check.outputs.skip != 'true' | |
| uses: actions/cache@v4 | |
| with: | |
| path: ${{ env.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - name: Setup Rust | |
| if: steps.check.outputs.skip != 'true' | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.rust_target }} | |
| - name: Setup Rust cache | |
| if: steps.check.outputs.skip != 'true' | |
| uses: swatinem/rust-cache@v2 | |
| with: | |
| workspaces: "./src-tauri -> target" | |
| shared-key: ${{ matrix.name }} | |
| - name: Install frontend dependencies | |
| if: steps.check.outputs.skip != 'true' | |
| run: pnpm install | |
| - name: Build API sidecar (Unix) | |
| if: steps.check.outputs.skip != 'true' && matrix.platform != 'windows' | |
| shell: bash | |
| run: | | |
| cd src-api | |
| mkdir -p dist | |
| bun build src/index.ts --compile --target=${{ matrix.bun_target }} --outfile dist/workany-api-${{ matrix.rust_target }} | |
| - name: Build API sidecar (Windows) | |
| if: steps.check.outputs.skip != 'true' && matrix.platform == 'windows' | |
| shell: bash | |
| run: | | |
| cd src-api | |
| mkdir -p dist | |
| bun build src/index.ts --compile --target=${{ matrix.bun_target }} --outfile dist/workany-api-${{ matrix.rust_target }}.exe | |
| - name: Bundle CLI tools (Unix) | |
| if: steps.check.outputs.skip != 'true' && matrix.platform != 'windows' | |
| shell: bash | |
| run: | | |
| cd src-api/dist | |
| mkdir -p cli-bundle | |
| cd cli-bundle | |
| # Download Node.js for target platform | |
| NODE_VERSION="22.2.0" | |
| case "${{ matrix.rust_target }}" in | |
| x86_64-unknown-linux-gnu) | |
| NODE_PLATFORM="linux" | |
| NODE_ARCH="x64" | |
| ;; | |
| x86_64-apple-darwin) | |
| NODE_PLATFORM="darwin" | |
| NODE_ARCH="x64" | |
| ;; | |
| aarch64-apple-darwin) | |
| NODE_PLATFORM="darwin" | |
| NODE_ARCH="arm64" | |
| ;; | |
| esac | |
| NODE_FILENAME="node-v${NODE_VERSION}-${NODE_PLATFORM}-${NODE_ARCH}" | |
| curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/${NODE_FILENAME}.tar.gz" | tar xz | |
| cp "${NODE_FILENAME}/bin/node" ./node | |
| chmod +x ./node | |
| rm -rf "${NODE_FILENAME}" | |
| # Install CLI packages | |
| echo '{"name":"cli-bundle","private":true,"type":"module"}' > package.json | |
| npm install @anthropic-ai/claude-code @openai/codex --registry=https://registry.npmmirror.com | |
| # Copy wasm files | |
| cp node_modules/@anthropic-ai/claude-code/*.wasm . 2>/dev/null || true | |
| # Clean up unused platform binaries | |
| case "${{ matrix.rust_target }}" in | |
| x86_64-unknown-linux-gnu) | |
| CODEX_KEEP="x86_64-unknown-linux-musl" | |
| CLAUDE_KEEP="x64-linux" | |
| ;; | |
| x86_64-apple-darwin) | |
| CODEX_KEEP="x86_64-apple-darwin" | |
| CLAUDE_KEEP="x64-darwin" | |
| ;; | |
| aarch64-apple-darwin) | |
| CODEX_KEEP="aarch64-apple-darwin" | |
| CLAUDE_KEEP="arm64-darwin" | |
| ;; | |
| esac | |
| # Clean @openai/codex vendor | |
| if [ -d "node_modules/@openai/codex/vendor" ]; then | |
| for dir in node_modules/@openai/codex/vendor/*; do | |
| dirname=$(basename "$dir") | |
| if [ "$dirname" != "$CODEX_KEEP" ] && [ -d "$dir" ]; then | |
| rm -rf "$dir" | |
| fi | |
| done | |
| fi | |
| # Clean @anthropic-ai/claude-code vendor/ripgrep | |
| if [ -d "node_modules/@anthropic-ai/claude-code/vendor/ripgrep" ]; then | |
| for item in node_modules/@anthropic-ai/claude-code/vendor/ripgrep/*; do | |
| itemname=$(basename "$item") | |
| if [ -d "$item" ] && [ "$itemname" != "$CLAUDE_KEEP" ]; then | |
| rm -rf "$item" | |
| fi | |
| done | |
| fi | |
| cd .. | |
| # Create claude launcher script using base64 decode (avoids escape issues) | |
| # Supports: macOS (Resources), Linux (/usr/lib/WorkAny/) | |
| echo "IyEvYmluL2Jhc2gKU0NSSVBUX0RJUj0iJChjZCAiJChkaXJuYW1lICIkMCIpIiAmJiBwd2QpIgpmb3IgRElSIGluICIkU0NSSVBUX0RJUi9jbGktYnVuZGxlIiBcCiAgICAgICAgICAgIiRTQ1JJUFRfRElSL191cF8vc3JjLWFwaS9kaXN0L2NsaS1idW5kbGUiIFwKICAgICAgICAgICAiJFNDUklQVF9ESVIvLi4vUmVzb3VyY2VzL191cF8vc3JjLWFwaS9kaXN0L2NsaS1idW5kbGUiIFwKICAgICAgICAgICAiJFNDUklQVF9ESVIvLi4vUmVzb3VyY2VzL2NsaS1idW5kbGUiIFwKICAgICAgICAgICAiJFNDUklQVF9ESVIvLi4vbGliL1dvcmtBbnkvX3VwXy9zcmMtYXBpL2Rpc3QvY2xpLWJ1bmRsZSIgXAogICAgICAgICAgICIkU0NSSVBUX0RJUi8uLi9saWIvd29ya2FueS9fdXBfL3NyYy1hcGkvZGlzdC9jbGktYnVuZGxlIjsgZG8KICAgIGlmIFsgLWYgIiRESVIvbm9kZSIgXSAmJiBbIC1kICIkRElSL25vZGVfbW9kdWxlcyIgXTsgdGhlbgogICAgICAgIEJVTkRMRV9ESVI9IiRESVIiCiAgICAgICAgYnJlYWsKICAgIGZpCmRvbmUKaWYgWyAteiAiJEJVTkRMRV9ESVIiIF07IHRoZW4KICAgIGVjaG8gIkVycm9yOiBjbGktYnVuZGxlIG5vdCBmb3VuZC4gU2VhcmNoZWQgcGF0aHM6IiA+JjIKICAgIGVjaG8gIiAgLSAkU0NSSVBUX0RJUi9jbGktYnVuZGxlIiA+JjIKICAgIGVjaG8gIiAgLSAkU0NSSVBUX0RJUi9fdXBfL3NyYy1hcGkvZGlzdC9jbGktYnVuZGxlIiA+JjIKICAgIGVjaG8gIiAgLSAkU0NSSVBUX0RJUi8uLi9SZXNvdXJjZXMvX3VwXy9zcmMtYXBpL2Rpc3QvY2xpLWJ1bmRsZSIgPiYyCiAgICBlY2hvICIgIC0gJFNDUklQVF9ESVIvLi4vbGliL1dvcmtBbnkvX3VwXy9zcmMtYXBpL2Rpc3QvY2xpLWJ1bmRsZSIgPiYyCiAgICBleGl0IDEKZmkKZXhlYyAiJEJVTkRMRV9ESVIvbm9kZSIgIiRCVU5ETEVfRElSL25vZGVfbW9kdWxlcy9AYW50aHJvcGljLWFpL2NsYXVkZS1jb2RlL2NsaS5qcyIgIiRAIgo=" | base64 -d > claude | |
| chmod +x claude | |
| cp claude "claude-${{ matrix.rust_target }}" | |
| # Create codex launcher script using base64 decode | |
| # Supports: macOS (Resources), Linux (/usr/lib/WorkAny/) | |
| echo "IyEvYmluL2Jhc2gKU0NSSVBUX0RJUj0iJChjZCAiJChkaXJuYW1lICIkMCIpIiAmJiBwd2QpIgpmb3IgRElSIGluICIkU0NSSVBUX0RJUi9jbGktYnVuZGxlIiBcCiAgICAgICAgICAgIiRTQ1JJUFRfRElSL191cF8vc3JjLWFwaS9kaXN0L2NsaS1idW5kbGUiIFwKICAgICAgICAgICAiJFNDUklQVF9ESVIvLi4vUmVzb3VyY2VzL191cF8vc3JjLWFwaS9kaXN0L2NsaS1idW5kbGUiIFwKICAgICAgICAgICAiJFNDUklQVF9ESVIvLi4vUmVzb3VyY2VzL2NsaS1idW5kbGUiIFwKICAgICAgICAgICAiJFNDUklQVF9ESVIvLi4vbGliL1dvcmtBbnkvX3VwXy9zcmMtYXBpL2Rpc3QvY2xpLWJ1bmRsZSIgXAogICAgICAgICAgICIkU0NSSVBUX0RJUi8uLi9saWIvd29ya2FueS9fdXBfL3NyYy1hcGkvZGlzdC9jbGktYnVuZGxlIjsgZG8KICAgIGlmIFsgLWYgIiRESVIvbm9kZSIgXSAmJiBbIC1kICIkRElSL25vZGVfbW9kdWxlcyIgXTsgdGhlbgogICAgICAgIEJVTkRMRV9ESVI9IiRESVIiCiAgICAgICAgYnJlYWsKICAgIGZpCmRvbmUKaWYgWyAteiAiJEJVTkRMRV9ESVIiIF07IHRoZW4KICAgIGVjaG8gIkVycm9yOiBjbGktYnVuZGxlIG5vdCBmb3VuZC4gU2VhcmNoZWQgcGF0aHM6IiA+JjIKICAgIGVjaG8gIiAgLSAkU0NSSVBUX0RJUi9jbGktYnVuZGxlIiA+JjIKICAgIGVjaG8gIiAgLSAkU0NSSVBUX0RJUi9fdXBfL3NyYy1hcGkvZGlzdC9jbGktYnVuZGxlIiA+JjIKICAgIGVjaG8gIiAgLSAkU0NSSVBUX0RJUi8uLi9SZXNvdXJjZXMvX3VwXy9zcmMtYXBpL2Rpc3QvY2xpLWJ1bmRsZSIgPiYyCiAgICBlY2hvICIgIC0gJFNDUklQVF9ESVIvLi4vbGliL1dvcmtBbnkvX3VwXy9zcmMtYXBpL2Rpc3QvY2xpLWJ1bmRsZSIgPiYyCiAgICBleGl0IDEKZmkKZXhlYyAiJEJVTkRMRV9ESVIvbm9kZSIgIiRCVU5ETEVfRElSL25vZGVfbW9kdWxlcy9Ab3BlbmFpL2NvZGV4L2Jpbi9jb2RleC5qcyIgIiRAIgo=" | base64 -d > codex | |
| chmod +x codex | |
| cp codex "codex-${{ matrix.rust_target }}" | |
| echo "CLI bundle created successfully" | |
| ls -la | |
| ls -la cli-bundle/ | |
| du -sh cli-bundle/ | |
| - name: Bundle CLI tools (Windows) | |
| if: steps.check.outputs.skip != 'true' && matrix.platform == 'windows' | |
| shell: bash | |
| run: | | |
| cd src-api/dist | |
| mkdir -p cli-bundle | |
| cd cli-bundle | |
| # Download Node.js for Windows | |
| NODE_VERSION="22.2.0" | |
| NODE_FILENAME="node-v${NODE_VERSION}-win-x64" | |
| curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/${NODE_FILENAME}.zip" -o node.zip | |
| unzip -q node.zip | |
| cp "${NODE_FILENAME}/node.exe" ./node.exe | |
| rm -rf "${NODE_FILENAME}" node.zip | |
| # Install CLI packages | |
| echo '{"name":"cli-bundle","private":true,"type":"module"}' > package.json | |
| npm install @anthropic-ai/claude-code @openai/codex --registry=https://registry.npmmirror.com | |
| # Copy wasm files | |
| cp node_modules/@anthropic-ai/claude-code/*.wasm . 2>/dev/null || true | |
| # Clean up unused platform binaries (keep Windows only) | |
| if [ -d "node_modules/@openai/codex/vendor" ]; then | |
| for dir in node_modules/@openai/codex/vendor/*; do | |
| dirname=$(basename "$dir") | |
| if [ "$dirname" != "x86_64-pc-windows-msvc" ] && [ -d "$dir" ]; then | |
| rm -rf "$dir" | |
| fi | |
| done | |
| fi | |
| if [ -d "node_modules/@anthropic-ai/claude-code/vendor/ripgrep" ]; then | |
| for item in node_modules/@anthropic-ai/claude-code/vendor/ripgrep/*; do | |
| itemname=$(basename "$item") | |
| if [ -d "$item" ] && [ "$itemname" != "x64-win32" ]; then | |
| rm -rf "$item" | |
| fi | |
| done | |
| fi | |
| cd .. | |
| # Create Windows batch launchers using base64 decode (avoids escape issues) | |
| echo "QGVjaG8gb2ZmDQpzZXRsb2NhbA0Kc2V0ICJTQ1JJUFRfRElSPSV+ZHAwIg0Kc2V0ICJCVU5ETEVfRElSPSVTQ1JJUFRfRElSJWNsaS1idW5kbGUiDQppZiBub3QgZXhpc3QgIiVCVU5ETEVfRElSJVxub2RlLmV4ZSIgc2V0ICJCVU5ETEVfRElSPSVTQ1JJUFRfRElSJS4uXFJlc291cmNlc1xjbGktYnVuZGxlIg0KIiVCVU5ETEVfRElSJVxub2RlLmV4ZSIgIiVCVU5ETEVfRElSJVxub2RlX21vZHVsZXNcQGFudGhyb3BpYy1haVxjbGF1ZGUtY29kZVxjbGkuanMiICUqDQo=" | base64 -d > claude.cmd | |
| cp claude.cmd "claude-${{ matrix.rust_target }}.cmd" | |
| echo "QGVjaG8gb2ZmDQpzZXRsb2NhbA0Kc2V0ICJTQ1JJUFRfRElSPSV+ZHAwIg0Kc2V0ICJCVU5ETEVfRElSPSVTQ1JJUFRfRElSJWNsaS1idW5kbGUiDQppZiBub3QgZXhpc3QgIiVCVU5ETEVfRElSJVxub2RlLmV4ZSIgc2V0ICJCVU5ETEVfRElSPSVTQ1JJUFRfRElSJS4uXFJlc291cmNlc1xjbGktYnVuZGxlIg0KIiVCVU5ETEVfRElSJVxub2RlLmV4ZSIgIiVCVU5ETEVfRElSJVxub2RlX21vZHVsZXNcQG9wZW5haVxjb2RleFxiaW5cY29kZXguanMiICUqDQo=" | base64 -d > codex.cmd | |
| cp codex.cmd "codex-${{ matrix.rust_target }}.cmd" | |
| echo "CLI bundle created successfully" | |
| ls -la | |
| ls -la cli-bundle/ | |
| du -sh cli-bundle/ | |
| - name: Sign CLI bundle binaries (macOS) | |
| if: steps.check.outputs.skip != 'true' && startsWith(matrix.platform, 'macos') | |
| env: | |
| APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} | |
| shell: bash | |
| run: | | |
| # Import certificate | |
| CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 | |
| KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db | |
| KEYCHAIN_PASSWORD=$(openssl rand -base64 32) | |
| ENTITLEMENTS_FILE="${GITHUB_WORKSPACE}/src-tauri/entitlements.plist" | |
| # Decode and save certificate | |
| echo -n "$APPLE_CERTIFICATE" | base64 --decode -o $CERTIFICATE_PATH | |
| # Create temporary keychain | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| security set-keychain-settings -lut 21600 $KEYCHAIN_PATH | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
| # Import certificate to keychain | |
| security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH | |
| security list-keychain -d user -s $KEYCHAIN_PATH | |
| # Sign all executable binaries in cli-bundle | |
| echo "Signing binaries in cli-bundle..." | |
| cd src-api/dist/cli-bundle | |
| # Sign node binary first with entitlements (required for JIT/V8) | |
| if [ -f "node" ]; then | |
| echo "Signing node with entitlements..." | |
| codesign --force --options runtime --entitlements "$ENTITLEMENTS_FILE" --sign "$APPLE_SIGNING_IDENTITY" --timestamp node | |
| fi | |
| # Sign ALL .dylib files (including @img/sharp-libvips-*) | |
| echo "Signing all .dylib files..." | |
| find . -name "*.dylib" -type f | while read -r file; do | |
| echo "Signing $file..." | |
| codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$file" || true | |
| done | |
| # Sign ALL .node files (including @img/sharp-*) | |
| echo "Signing all .node files..." | |
| find . -name "*.node" -type f | while read -r file; do | |
| echo "Signing $file..." | |
| codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$file" || true | |
| done | |
| # Sign ALL .so files | |
| echo "Signing all .so files..." | |
| find . -name "*.so" -type f | while read -r file; do | |
| echo "Signing $file..." | |
| codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$file" || true | |
| done | |
| # Sign ripgrep and other Mach-O executables | |
| echo "Signing Mach-O executable binaries..." | |
| find . -type f \( -name "rg" -o -perm -u+x \) 2>/dev/null | while read -r file; do | |
| # Skip non-binary files | |
| case "$file" in | |
| *.js|*.json|*.md|*.txt|*.ts|*.mjs|*.cjs|*.map|*.yml|*.yaml) continue ;; | |
| esac | |
| # Skip node (already signed with entitlements above) | |
| filename=$(basename "$file") | |
| if [ "$filename" = "node" ]; then | |
| continue | |
| fi | |
| # Check if it's a Mach-O binary | |
| if file "$file" 2>/dev/null | grep -q "Mach-O"; then | |
| echo "Signing Mach-O: $file..." | |
| codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$file" || true | |
| fi | |
| done | |
| cd ../../.. | |
| # Also sign the launcher scripts (they are executables) | |
| echo "Signing launcher scripts..." | |
| codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp src-api/dist/claude | |
| codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp src-api/dist/codex | |
| echo "All binaries signed successfully" | |
| # Cleanup | |
| security delete-keychain $KEYCHAIN_PATH | |
| rm -f $CERTIFICATE_PATH | |
| - name: Update tauri.conf.json for CLI bundle (Unix) | |
| if: steps.check.outputs.skip != 'true' && matrix.platform != 'windows' | |
| shell: bash | |
| run: | | |
| node -e " | |
| const fs = require('fs'); | |
| const config = JSON.parse(fs.readFileSync('src-tauri/tauri.conf.json', 'utf8')); | |
| if (!config.bundle.externalBin) config.bundle.externalBin = []; | |
| if (!config.bundle.resources) config.bundle.resources = []; | |
| // Add CLI launchers (Unix only - they are shell scripts) | |
| if (!config.bundle.externalBin.includes('../src-api/dist/claude')) { | |
| config.bundle.externalBin.unshift('../src-api/dist/claude'); | |
| } | |
| if (!config.bundle.externalBin.includes('../src-api/dist/codex')) { | |
| config.bundle.externalBin.unshift('../src-api/dist/codex'); | |
| } | |
| // Add cli-bundle as resource | |
| const cliResource = '../src-api/dist/cli-bundle/**/*'; | |
| if (!config.bundle.resources.includes(cliResource)) { | |
| config.bundle.resources = config.bundle.resources.filter(r => | |
| !r.includes('claude-bundle') && !r.includes('codex-bundle') && !r.includes('cli-bundle') | |
| ); | |
| config.bundle.resources.push(cliResource); | |
| } | |
| fs.writeFileSync('src-tauri/tauri.conf.json', JSON.stringify(config, null, 2)); | |
| console.log('Updated tauri.conf.json with CLI bundle config (Unix)'); | |
| " | |
| - name: Update tauri.conf.json for CLI bundle (Windows) | |
| if: steps.check.outputs.skip != 'true' && matrix.platform == 'windows' | |
| shell: bash | |
| run: | | |
| node -e " | |
| const fs = require('fs'); | |
| const config = JSON.parse(fs.readFileSync('src-tauri/tauri.conf.json', 'utf8')); | |
| if (!config.bundle.resources) config.bundle.resources = []; | |
| // Windows: Don't add to externalBin (Tauri expects .exe files) | |
| // Remove any existing claude/codex from externalBin | |
| if (config.bundle.externalBin) { | |
| config.bundle.externalBin = config.bundle.externalBin.filter(bin => | |
| !bin.includes('claude') && !bin.includes('codex') | |
| ); | |
| } | |
| // Add cli-bundle as resource | |
| const cliResource = '../src-api/dist/cli-bundle/**/*'; | |
| if (!config.bundle.resources.includes(cliResource)) { | |
| config.bundle.resources = config.bundle.resources.filter(r => | |
| !r.includes('claude-bundle') && !r.includes('codex-bundle') && !r.includes('cli-bundle') | |
| ); | |
| config.bundle.resources.push(cliResource); | |
| } | |
| // Also add .cmd launcher files as resources for Windows | |
| config.bundle.resources.push('../src-api/dist/claude.cmd'); | |
| config.bundle.resources.push('../src-api/dist/codex.cmd'); | |
| fs.writeFileSync('src-tauri/tauri.conf.json', JSON.stringify(config, null, 2)); | |
| console.log('Updated tauri.conf.json with CLI bundle config (Windows - resources only)'); | |
| " | |
| - name: Build frontend | |
| if: steps.check.outputs.skip != 'true' | |
| run: pnpm build | |
| - name: Build Tauri app (Linux - skip AppImage) | |
| if: steps.check.outputs.skip != 'true' && matrix.platform == 'linux' | |
| uses: tauri-apps/tauri-action@v0 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| tauriScript: pnpm tauri | |
| args: --target ${{ matrix.tauri_target }} --bundles deb,rpm | |
| - name: Build Tauri app (Windows/macOS) | |
| if: steps.check.outputs.skip != 'true' && matrix.platform != 'linux' | |
| uses: tauri-apps/tauri-action@v0 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # macOS signing and notarization | |
| APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| with: | |
| tauriScript: pnpm tauri | |
| args: --target ${{ matrix.tauri_target }} | |
| - name: Upload artifacts (Linux) | |
| if: steps.check.outputs.skip != 'true' && matrix.platform == 'linux' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: WorkAny-${{ matrix.name }} | |
| path: | | |
| src-tauri/target/${{ matrix.tauri_target }}/release/bundle/deb/*.deb | |
| src-tauri/target/${{ matrix.tauri_target }}/release/bundle/rpm/*.rpm | |
| if-no-files-found: ignore | |
| - name: Upload artifacts (Windows) | |
| if: steps.check.outputs.skip != 'true' && matrix.platform == 'windows' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: WorkAny-${{ matrix.name }} | |
| path: | | |
| src-tauri/target/${{ matrix.tauri_target }}/release/bundle/msi/*.msi | |
| src-tauri/target/${{ matrix.tauri_target }}/release/bundle/nsis/*.exe | |
| if-no-files-found: ignore | |
| - name: Upload artifacts (macOS) | |
| if: steps.check.outputs.skip != 'true' && startsWith(matrix.platform, 'macos') | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: WorkAny-${{ matrix.name }} | |
| path: | | |
| src-tauri/target/${{ matrix.tauri_target }}/release/bundle/dmg/*.dmg | |
| src-tauri/target/${{ matrix.tauri_target }}/release/bundle/macos/*.app | |
| if-no-files-found: ignore | |
| release: | |
| needs: build | |
| runs-on: ubuntu-latest | |
| # Auto release on: push to main or manual trigger with release=true | |
| if: github.ref == 'refs/heads/main' || github.event.inputs.release == 'true' | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Display structure of downloaded files | |
| run: | | |
| ls -R artifacts | |
| echo "---" | |
| find artifacts -type f -name "*.deb" -o -name "*.rpm" -o -name "*.msi" -o -name "*.exe" -o -name "*.dmg" | head -20 | |
| - name: Checkout for version | |
| uses: actions/checkout@v4 | |
| with: | |
| path: repo | |
| - name: Get version from package.json | |
| id: version | |
| run: | | |
| VERSION=$(node -p "require('./repo/package.json').version") | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| - name: Flatten artifacts for release | |
| run: | | |
| mkdir -p release-files | |
| find artifacts -type f \( -name "*.deb" -o -name "*.rpm" -o -name "*.msi" -o -name "*.exe" -o -name "*.dmg" \) -exec cp {} release-files/ \; | |
| ls -la release-files/ | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| draft: true | |
| generate_release_notes: true | |
| tag_name: v${{ steps.version.outputs.version }} | |
| name: WorkAny v${{ steps.version.outputs.version }} | |
| files: release-files/* | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |