name: Release macOS on: push: tags: - "v*" permissions: contents: write jobs: # =================================================================== # Build Yao macOS binaries (arm64 + amd64) — one job, both arches # =================================================================== build: runs-on: macos-latest steps: - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 18 - name: Install pnpm run: npm install -g pnpm - name: Setup Cache uses: actions/cache@v4 with: path: | ~/.cache/go-build ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - name: Checkout Kun uses: actions/checkout@v4 with: repository: yaoapp/kun path: kun - name: Checkout Xun uses: actions/checkout@v4 with: repository: yaoapp/xun path: xun - name: Checkout Gou uses: actions/checkout@v4 with: repository: yaoapp/gou path: gou - name: Checkout V8Go uses: actions/checkout@v4 with: repository: yaoapp/v8go path: v8go - name: Unzip libv8 run: | files=$(find ./v8go -name "libv8*.zip") for file in $files; do dir=$(dirname "$file") echo "Extracting $file to directory $dir" unzip -o -d $dir $file rm -rf $dir/__MACOSX done - name: Checkout CUI v1.0 uses: actions/checkout@v4 with: repository: yaoapp/cui path: cui-v1.0 - name: Checkout Yao-Init uses: actions/checkout@v4 with: repository: yaoapp/yao-init path: yao-init - name: Move Dependencies run: | mv kun ../ mv xun ../ mv gou ../ mv v8go ../ mv cui-v1.0 ../ mv yao-init ../ rm -f ../cui-v1.0/packages/setup/vite.config.ts.* - name: Checkout Yao uses: actions/checkout@v4 - name: Set Version from Tag run: | if [[ "$GITHUB_REF" != refs/tags/v* ]]; then echo "::error::This workflow requires a tag (refs/tags/v*). Got: $GITHUB_REF" exit 1 fi TAG="${GITHUB_REF#refs/tags/v}" echo "Setting VERSION to $TAG" sed -i.bak "s/const VERSION = \".*\"/const VERSION = \"${TAG}\"/g" share/const.go rm -f share/const.go.bak grep 'const VERSION' share/const.go - name: Setup Go uses: actions/setup-go@v5 with: go-version: "1.25" - name: Setup Go Tools run: make tools - name: Make Artifacts macOS run: make artifacts-macos - name: Get Version id: version run: | VERSION=$(grep 'const VERSION =' share/const.go | awk '{print $4}' | sed 's/"//g') echo "version=${VERSION}" >> $GITHUB_OUTPUT - name: List Build Output run: ls -lh dist/release/ - name: Install Certificates env: KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} run: | mkdir -p certs echo "${{ secrets.APPLE_DEVELOPERIDG2CA }}" | base64 --decode > certs/DeveloperIDG2CA.cer echo "${{ secrets.APPLE_DISTRIBUTION }}" | base64 --decode > certs/distribution.cer echo "${{ secrets.APPLE_PRIVATE_KEY }}" | base64 --decode > certs/private_key.p12 security verify-cert -c certs/DeveloperIDG2CA.cer security verify-cert -c certs/distribution.cer - name: Import Certificates env: KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} run: | KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security set-keychain-settings -lut 21600 $KEYCHAIN_PATH security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security import ./certs/DeveloperIDG2CA.cer -k $KEYCHAIN_PATH -T /usr/bin/codesign security import ./certs/distribution.cer -k $KEYCHAIN_PATH -T /usr/bin/codesign security import ./certs/private_key.p12 -k $KEYCHAIN_PATH -P "${{ secrets.APPLE_PRIVATE_KEY_PASSWORD }}" -T /usr/bin/codesign security list-keychain -d user -s $KEYCHAIN_PATH - name: Sign Yao Binaries run: | VERSION="${{ steps.version.outputs.version }}" IDENTITY="Developer ID Application: ${{ secrets.APPLE_SIGN }}" for ARCH in arm64 amd64; do for SUFFIX in "" "-prod"; do BIN="dist/release/yao-${VERSION}-darwin-${ARCH}${SUFFIX}" codesign --force --verbose --timestamp --options runtime \ --entitlements .github/codesign/entitlements.plist \ --sign "$IDENTITY" "$BIN" codesign --verify --deep --strict --verbose=2 "$BIN" done done - name: Prepare Output and Checksums run: | VERSION="${{ steps.version.outputs.version }}" for ARCH in arm64 amd64; do for VARIANT in dev prod; do if [ "$VARIANT" = "dev" ]; then SRC="dist/release/yao-${VERSION}-darwin-${ARCH}" else SRC="dist/release/yao-${VERSION}-darwin-${ARCH}-prod" fi DIR="/tmp/yao-output-${ARCH}-${VARIANT}" mkdir -p "$DIR" cp "$SRC" "$DIR/yao" chmod +x "$DIR/yao" done done mkdir -p /tmp/checksums for ARCH in arm64 amd64; do for VARIANT in dev prod; do shasum -a 256 "/tmp/yao-output-${ARCH}-${VARIANT}/yao" | awk '{print $1" yao"}' > "/tmp/checksums/yao-darwin-${ARCH}-${VARIANT}.sha256" done done cat /tmp/checksums/*.sha256 - name: Upload Artifacts uses: actions/upload-artifact@v4 with: name: yao-darwin-arm64 path: /tmp/yao-output-arm64-prod/yao - name: Upload arm64 Dev Binary uses: actions/upload-artifact@v4 with: name: yao-darwin-arm64-dev path: /tmp/yao-output-arm64-dev/yao - name: Upload amd64 Binary uses: actions/upload-artifact@v4 with: name: yao-darwin-amd64 path: /tmp/yao-output-amd64-prod/yao - name: Upload amd64 Dev Binary uses: actions/upload-artifact@v4 with: name: yao-darwin-amd64-dev path: /tmp/yao-output-amd64-dev/yao - name: Upload Checksums uses: actions/upload-artifact@v4 with: name: yao-darwin-checksums path: /tmp/checksums/*.sha256 # =================================================================== # GitHub Release + R2 Upload (macOS binaries) # =================================================================== release: needs: build if: startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest steps: - name: Get Version id: version run: | if [[ "$GITHUB_REF" != refs/tags/v* ]]; then echo "::error::This workflow requires a tag. Got: $GITHUB_REF" exit 1 fi VERSION="${GITHUB_REF#refs/tags/v}" TAG="${GITHUB_REF#refs/tags/}" echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "tag=${TAG}" >> $GITHUB_OUTPUT - name: Download macOS Artifacts uses: actions/download-artifact@v4 with: name: yao-darwin-arm64 path: artifacts/arm64-prod - name: Download arm64 Dev uses: actions/download-artifact@v4 with: name: yao-darwin-arm64-dev path: artifacts/arm64-dev - name: Download amd64 Prod uses: actions/download-artifact@v4 with: name: yao-darwin-amd64 path: artifacts/amd64-prod - name: Download amd64 Dev uses: actions/download-artifact@v4 with: name: yao-darwin-amd64-dev path: artifacts/amd64-dev - name: Download Checksums uses: actions/download-artifact@v4 with: name: yao-darwin-checksums path: artifacts/checksums - name: Prepare Release Files run: | VERSION="${{ steps.version.outputs.version }}" mkdir -p release cp artifacts/arm64-prod/yao "release/yao-${VERSION}-darwin-arm64" cp artifacts/amd64-prod/yao "release/yao-${VERSION}-darwin-amd64" cp artifacts/arm64-dev/yao "release/yao-${VERSION}-darwin-arm64-dev" cp artifacts/amd64-dev/yao "release/yao-${VERSION}-darwin-amd64-dev" cp artifacts/checksums/*.sha256 release/ 2>/dev/null || true chmod +x release/yao-* ls -lh release/ - name: Wait for Draft Release env: GH_TOKEN: ${{ github.token }} run: | TAG="${{ steps.version.outputs.tag }}" for i in $(seq 1 30); do if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" &>/dev/null; then echo "Draft release found for $TAG." exit 0 fi echo "Waiting for draft release... ($i/30)" sleep 10 done echo "::error::Timed out waiting for draft release $TAG" exit 1 - name: Upload Assets to GitHub Release env: GH_TOKEN: ${{ github.token }} run: | TAG="${{ steps.version.outputs.tag }}" gh release upload "$TAG" release/* --repo "$GITHUB_REPOSITORY" --clobber - name: Publish Release if Complete env: GH_TOKEN: ${{ github.token }} run: | TAG="${{ steps.version.outputs.tag }}" ASSET_COUNT=$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets --jq '.assets | length') echo "Current assets: $ASSET_COUNT / 16" if [ "$ASSET_COUNT" -ge 16 ]; then echo "All assets present, publishing release..." gh release edit "$TAG" --repo "$GITHUB_REPOSITORY" --draft=false --latest else echo "Assets incomplete ($ASSET_COUNT/16), waiting for other workflow to publish." fi - name: Upload macOS binaries to R2 env: AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} R2_ENDPOINTS: ${{ secrets.R2_ENDPOINTS }} R2_BUCKET: ${{ secrets.R2_BUCKET || 'releases' }} run: | aws configure set default.region us-east-1 aws configure set default.s3.signature_version s3v4 VERSION="${{ steps.version.outputs.version }}" PREFIX="yao/${VERSION}" for PLATFORM in darwin-arm64 darwin-amd64; do FILE="release/yao-${VERSION}-${PLATFORM}" NAME="yao-${VERSION}-${PLATFORM}" sha256sum "$FILE" | awk '{print $1}' > "/tmp/${NAME}.sha256" aws s3 cp "$FILE" "s3://${R2_BUCKET}/${PREFIX}/${NAME}" \ --endpoint-url "$R2_ENDPOINTS" \ --content-type "application/octet-stream" aws s3 cp "/tmp/${NAME}.sha256" "s3://${R2_BUCKET}/${PREFIX}/${NAME}.sha256" \ --endpoint-url "$R2_ENDPOINTS" \ --content-type "text/plain" echo "Uploaded: ${NAME} + ${NAME}.sha256" done