fix: keep agent detection portable #65
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 & Release | |
| on: | |
| push: | |
| branches: [main, master] | |
| tags: ["v*"] | |
| pull_request: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| release_tag: | |
| description: Existing v* tag to build and publish | |
| required: false | |
| type: string | |
| permissions: | |
| contents: read | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: "1" | |
| RELEASE_TAG: ${{ github.event.release.tag_name || inputs.release_tag || github.ref_name }} | |
| jobs: | |
| quality: | |
| name: Quality gates | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.event.release.tag_name || inputs.release_tag || github.sha }} | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| - name: Cache Cargo | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Format | |
| run: cargo fmt --check | |
| - name: Check | |
| run: cargo check --locked | |
| - name: Validate Design, traceability, and Product Scope contract | |
| run: >- | |
| cargo run --quiet --locked -- | |
| --workspace "$PWD" | |
| --read-only | |
| --no-exec | |
| --allow-sleep | |
| intelligence --check --json | |
| - name: Validate release version consistency | |
| if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'release' || inputs.release_tag != '' | |
| shell: bash | |
| run: | | |
| package_version=$(sed -n 's/^version = "\(.*\)"$/\1/p' Cargo.toml | head -1) | |
| full_tag="v${package_version}" | |
| short_tag="v${package_version%.0}" | |
| test "${RELEASE_TAG}" = "${full_tag}" || test "${RELEASE_TAG}" = "${short_tag}" | |
| for manifest in \ | |
| marketplace.json \ | |
| wcode-agent-plugin/marketplace.json \ | |
| wcode-agent-plugin/plugin.json \ | |
| wcode-agent-plugin/.claude-plugin/plugin.json \ | |
| wcode-agent-plugin/.codex-plugin/plugin.json \ | |
| wcode-agent-plugin/.zcode-plugin/plugin.json | |
| do | |
| grep -q '"version": "'"${package_version}"'"' "${manifest}" | |
| done | |
| - name: Clippy | |
| run: cargo clippy --locked --all-targets -- -D warnings | |
| - name: Validate Unix installer | |
| run: sh -n install.sh | |
| test: | |
| name: Test ${{ matrix.name }} | |
| needs: quality | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: Linux x86_64 | |
| os: ubuntu-latest | |
| - name: macOS | |
| os: macos-latest | |
| - name: Windows x86_64 | |
| os: windows-latest | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.event.release.tag_name || inputs.release_tag || github.sha }} | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Cargo | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Test | |
| run: cargo test --locked | |
| - name: Validate Windows installer | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: '[scriptblock]::Create((Get-Content -Raw install.ps1)) | Out-Null' | |
| build: | |
| name: Build release ${{ matrix.name }} | |
| if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'release' || inputs.release_tag != '' | |
| needs: test | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: Linux x86_64 | |
| os: ubuntu-latest | |
| platform: linux | |
| artifact: wcode-linux-x86_64 | |
| archive: wcode-linux-x86_64.tar.gz | |
| - name: macOS Universal | |
| os: macos-latest | |
| platform: macos | |
| artifact: wcode-macos-universal | |
| archive: wcode-macos-universal.tar.gz | |
| - name: Windows x86_64 | |
| os: windows-latest | |
| platform: windows | |
| artifact: wcode-windows-x86_64 | |
| archive: wcode-windows-x86_64.zip | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.event.release.tag_name || inputs.release_tag || github.sha }} | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Cargo | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Build Linux | |
| if: matrix.platform == 'linux' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p dist/package dist/candidates | |
| for opt in z s; do | |
| CARGO_PROFILE_RELEASE_OPT_LEVEL="$opt" cargo build --release --locked | |
| install -m 755 target/release/wcode "dist/candidates/wcode-${opt}" | |
| done | |
| z_bytes=$(stat -c%s dist/candidates/wcode-z) | |
| s_bytes=$(stat -c%s dist/candidates/wcode-s) | |
| if (( s_bytes < z_bytes )); then | |
| selected_opt=s | |
| else | |
| selected_opt=z | |
| fi | |
| install -m 755 "dist/candidates/wcode-${selected_opt}" dist/package/wcode | |
| cp README.md dist/package/README.md | |
| dist/package/wcode --version | |
| tar -C dist/package -cf - . | gzip -9n > "dist/${{ matrix.archive }}" | |
| binary_bytes=$(stat -c%s dist/package/wcode) | |
| archive_bytes=$(stat -c%s "dist/${{ matrix.archive }}") | |
| { | |
| echo "### ${{ matrix.name }}" | |
| echo "- Size candidates: z=${z_bytes} bytes, s=${s_bytes} bytes" | |
| echo "- Selected opt-level: ${selected_opt}" | |
| echo "- Binary: ${binary_bytes} bytes" | |
| echo "- Archive: ${archive_bytes} bytes" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Build macOS universal binary | |
| if: matrix.platform == 'macos' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| rustup target add aarch64-apple-darwin x86_64-apple-darwin | |
| mkdir -p dist/candidates dist/package-universal dist/package-arm64 dist/package-x86_64 | |
| for target in aarch64-apple-darwin x86_64-apple-darwin; do | |
| for opt in z s; do | |
| CARGO_PROFILE_RELEASE_OPT_LEVEL="$opt" cargo build --release --locked --target "$target" | |
| install -m 755 "target/${target}/release/wcode" "dist/candidates/wcode-${target}-${opt}" | |
| done | |
| done | |
| arm64_z_bytes=$(stat -f%z dist/candidates/wcode-aarch64-apple-darwin-z) | |
| arm64_s_bytes=$(stat -f%z dist/candidates/wcode-aarch64-apple-darwin-s) | |
| if (( arm64_s_bytes < arm64_z_bytes )); then | |
| arm64_opt=s | |
| else | |
| arm64_opt=z | |
| fi | |
| x86_64_z_bytes=$(stat -f%z dist/candidates/wcode-x86_64-apple-darwin-z) | |
| x86_64_s_bytes=$(stat -f%z dist/candidates/wcode-x86_64-apple-darwin-s) | |
| if (( x86_64_s_bytes < x86_64_z_bytes )); then | |
| x86_64_opt=s | |
| else | |
| x86_64_opt=z | |
| fi | |
| install -m 755 "dist/candidates/wcode-aarch64-apple-darwin-${arm64_opt}" dist/package-arm64/wcode | |
| install -m 755 "dist/candidates/wcode-x86_64-apple-darwin-${x86_64_opt}" dist/package-x86_64/wcode | |
| lipo -create \ | |
| dist/package-arm64/wcode \ | |
| dist/package-x86_64/wcode \ | |
| -output dist/package-universal/wcode | |
| chmod +x dist/package-universal/wcode | |
| cp README.md dist/package-universal/README.md | |
| cp README.md dist/package-arm64/README.md | |
| cp README.md dist/package-x86_64/README.md | |
| dist/package-universal/wcode --version | |
| COPYFILE_DISABLE=1 tar -C dist/package-universal -cf - . | gzip -9n > "dist/${{ matrix.archive }}" | |
| COPYFILE_DISABLE=1 tar -C dist/package-arm64 -cf - . | gzip -9n > dist/wcode-macos-aarch64.tar.gz | |
| COPYFILE_DISABLE=1 tar -C dist/package-x86_64 -cf - . | gzip -9n > dist/wcode-macos-x86_64.tar.gz | |
| universal_binary_bytes=$(stat -f%z dist/package-universal/wcode) | |
| universal_archive_bytes=$(stat -f%z "dist/${{ matrix.archive }}") | |
| arm64_binary_bytes=$(stat -f%z dist/package-arm64/wcode) | |
| arm64_archive_bytes=$(stat -f%z dist/wcode-macos-aarch64.tar.gz) | |
| x86_64_binary_bytes=$(stat -f%z dist/package-x86_64/wcode) | |
| x86_64_archive_bytes=$(stat -f%z dist/wcode-macos-x86_64.tar.gz) | |
| { | |
| echo "### ${{ matrix.name }}" | |
| echo "- Apple Silicon candidates: z=${arm64_z_bytes} bytes, s=${arm64_s_bytes} bytes; selected ${arm64_opt}" | |
| echo "- Intel candidates: z=${x86_64_z_bytes} bytes, s=${x86_64_s_bytes} bytes; selected ${x86_64_opt}" | |
| echo "- Universal binary: ${universal_binary_bytes} bytes" | |
| echo "- Universal archive: ${universal_archive_bytes} bytes" | |
| echo "- Apple Silicon binary: ${arm64_binary_bytes} bytes" | |
| echo "- Apple Silicon archive: ${arm64_archive_bytes} bytes" | |
| echo "- Intel binary: ${x86_64_binary_bytes} bytes" | |
| echo "- Intel archive: ${x86_64_archive_bytes} bytes" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Build Windows | |
| if: matrix.platform == 'windows' | |
| shell: pwsh | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| New-Item -ItemType Directory -Force -Path dist/package, dist/candidates | Out-Null | |
| $candidateBytes = @{} | |
| foreach ($opt in @("z", "s")) { | |
| $env:CARGO_PROFILE_RELEASE_OPT_LEVEL = $opt | |
| cargo build --release --locked | |
| $candidate = "dist/candidates/wcode-$opt.exe" | |
| Copy-Item target/release/wcode.exe $candidate | |
| $candidateBytes[$opt] = (Get-Item $candidate).Length | |
| } | |
| if ($candidateBytes["s"] -lt $candidateBytes["z"]) { | |
| $selectedOpt = "s" | |
| } else { | |
| $selectedOpt = "z" | |
| } | |
| Copy-Item "dist/candidates/wcode-$selectedOpt.exe" dist/package/wcode.exe | |
| Copy-Item README.md dist/package/README.md | |
| & dist/package/wcode.exe --version | |
| Compress-Archive -Path dist/package/* -DestinationPath "dist/${{ matrix.archive }}" -CompressionLevel Optimal -Force | |
| $binaryBytes = (Get-Item dist/package/wcode.exe).Length | |
| $archiveBytes = (Get-Item "dist/${{ matrix.archive }}").Length | |
| @( | |
| "### ${{ matrix.name }}", | |
| "- Size candidates: z=$($candidateBytes['z']) bytes, s=$($candidateBytes['s']) bytes", | |
| "- Selected opt-level: $selectedOpt", | |
| "- Binary: $binaryBytes bytes", | |
| "- Archive: $archiveBytes bytes" | |
| ) | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append -Encoding utf8 | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ${{ matrix.artifact }} | |
| path: dist/${{ matrix.archive }} | |
| if-no-files-found: error | |
| compression-level: 0 | |
| retention-days: 14 | |
| - name: Upload macOS slim artifacts | |
| if: matrix.platform == 'macos' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wcode-macos-slim | |
| path: | | |
| dist/wcode-macos-aarch64.tar.gz | |
| dist/wcode-macos-x86_64.tar.gz | |
| if-no-files-found: error | |
| compression-level: 0 | |
| retention-days: 14 | |
| release: | |
| name: Publish GitHub Release | |
| if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'release' || inputs.release_tag != '' | |
| needs: build | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| path: dist | |
| merge-multiple: true | |
| - name: Generate checksums | |
| run: | | |
| cd dist | |
| sha256sum *.tar.gz *.zip > SHA256SUMS | |
| - name: Publish release | |
| uses: softprops/action-gh-release@v3 | |
| with: | |
| tag_name: ${{ env.RELEASE_TAG }} | |
| generate_release_notes: true | |
| files: | | |
| dist/*.tar.gz | |
| dist/*.zip | |
| dist/SHA256SUMS |