Skip to content

fix: route Custom LLM provider through OpenAI-compatible endpoint #137

fix: route Custom LLM provider through OpenAI-compatible endpoint

fix: route Custom LLM provider through OpenAI-compatible endpoint #137

Workflow file for this run

name: Build and Release
on:
push:
branches-ignore:
- main
tags:
- "v*.*.*"
- "v*.*.*-beta.*"
pull_request:
branches:
- main
permissions:
contents: write
pages: write
id-token: write
env:
XCODE_VERSION: "26.0.1"
BUNDLE_ID: "com.ameba.TRex"
APP_NAME: "TRex"
BETA_FEED_URL: "https://amebalabs.github.io/TRex/appcast_beta.xml"
PROD_FEED_URL: "https://amebalabs.github.io/TRex/appcast.xml"
jobs:
verify-release-notes:
if: startsWith(github.ref, 'refs/tags/v') && github.event_name != 'pull_request'
runs-on: ubuntu-latest
outputs:
release_notes: ${{ steps.read_notes.outputs.content }}
steps:
- uses: actions/checkout@v3
- name: Get version
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Check release notes exist
id: check_notes
run: |
VERSION=${{ steps.get_version.outputs.VERSION }}
NOTES_PATH="docs/release-notes/${VERSION}.md"
if [ ! -f "$NOTES_PATH" ]; then
echo "Error: Release notes not found for version ${VERSION}"
echo "Expected path: ${NOTES_PATH}"
exit 1
fi
- name: Read release notes
id: read_notes
run: |
VERSION=${{ steps.get_version.outputs.VERSION }}
NOTES_PATH="docs/release-notes/${VERSION}.md"
CONTENT=$(cat "$NOTES_PATH" | perl -p -e 's/%/%25/g' | perl -p -e 's/\n/%0A/g' | perl -p -e 's/\r/%0D/g')
echo "content=$CONTENT" >> $GITHUB_OUTPUT
build:
runs-on: macos-latest
timeout-minutes: 30
outputs:
build_number: ${{ steps.get_versions.outputs.BUILD_NUMBER }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Get Version Numbers
id: get_versions
run: |
# Get build number from commit count
BUILD_NUMBER=$(git rev-list --count HEAD)
echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_OUTPUT
# Initialize version variables
VERSION="0.0.0"
IS_RELEASE=false
# Determine if this is a release tag
if [ "${GITHUB_REF_TYPE}" = "tag" ] && [[ "${GITHUB_REF_NAME}" == v* ]]; then
VERSION=${GITHUB_REF_NAME#v}
IS_RELEASE=true
fi
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "IS_RELEASE=$IS_RELEASE" >> $GITHUB_OUTPUT
# Debug logging
echo "Debug information:"
echo "GITHUB_REF_TYPE: ${GITHUB_REF_TYPE}"
echo "GITHUB_REF_NAME: ${GITHUB_REF_NAME}"
echo "GITHUB_REF: ${GITHUB_REF}"
echo "BUILD_NUMBER: ${BUILD_NUMBER}"
echo "VERSION: ${VERSION}"
echo "IS_RELEASE: ${IS_RELEASE}"
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ env.XCODE_VERSION }}
- name: Build Application
env:
MARKETING_VERSION: ${{ steps.get_versions.outputs.VERSION }}
CURRENT_PROJECT_VERSION: ${{ steps.get_versions.outputs.BUILD_NUMBER }}
run: |
# Build universal (arm64 + x86_64) so the app launches on Intel Macs.
# TesseractSwift now ships universal xcframeworks, lifting the prior
# arm64-only constraint.
xcodebuild \
-scheme "${{ env.APP_NAME }}" \
-configuration Release \
-derivedDataPath build \
-arch arm64 -arch x86_64 \
-skipMacroValidation \
-skipPackagePluginValidation \
ONLY_ACTIVE_ARCH=NO \
MARKETING_VERSION="$MARKETING_VERSION" \
CURRENT_PROJECT_VERSION="$CURRENT_PROJECT_VERSION" \
build
- name: Build CLI
env:
MARKETING_VERSION: ${{ steps.get_versions.outputs.VERSION }}
CURRENT_PROJECT_VERSION: ${{ steps.get_versions.outputs.BUILD_NUMBER }}
run: |
# Build universal (arm64 + x86_64). TesseractSwift now ships universal
# xcframeworks, so the CLI no longer needs to be arm64-only.
xcodebuild \
-scheme "TRex CLI" \
-configuration Release \
-derivedDataPath build-cli \
-arch arm64 -arch x86_64 \
-skipMacroValidation \
-skipPackagePluginValidation \
ONLY_ACTIVE_ARCH=NO \
MARKETING_VERSION="$MARKETING_VERSION" \
CURRENT_PROJECT_VERSION="$CURRENT_PROJECT_VERSION" \
build
- name: Verify App Binary
run: |
echo "Checking app binary architectures..."
APP_BINARY="build/Build/Products/Release/${{ env.APP_NAME }}.app/Contents/MacOS/${{ env.APP_NAME }}"
ARCHS=$(lipo -archs "$APP_BINARY")
echo "Found architectures: $ARCHS"
# The app must be universal so it launches on both Apple Silicon and Intel.
for arch in arm64 x86_64; do
if ! echo "$ARCHS" | grep -qw "$arch"; then
echo "❌ Error: $arch architecture missing from app binary"
exit 1
fi
done
echo "✅ App binary is universal (arm64 + x86_64)"
file "$APP_BINARY"
- name: Prepare CLI Binary
run: |
# The CLI binary is named 'trex' and located in the Release directory
CLI_PATH="build-cli/Build/Products/Release/trex"
if [ ! -f "$CLI_PATH" ]; then
echo "❌ Error: CLI binary not found at $CLI_PATH"
echo "Contents of build-cli/Build/Products/Release/:"
ls -la "build-cli/Build/Products/Release/" || true
exit 1
fi
echo "Found CLI binary, size: $(ls -lh "$CLI_PATH" | awk '{print $5}')"
file "$CLI_PATH"
CLI_CACHE_DIR=".cli-cache"
mkdir -p "$CLI_CACHE_DIR"
CACHE_CLI_PATH="$CLI_CACHE_DIR/trex"
# Copy the binary into the cache directory
cp "$CLI_PATH" "$CACHE_CLI_PATH"
chmod +x "$CACHE_CLI_PATH"
echo "Cached binary info:"
ls -lh "$CACHE_CLI_PATH"
file "$CACHE_CLI_PATH"
echo "Checking CLI binary architectures..."
ARCHS=$(lipo -archs "$CACHE_CLI_PATH")
echo "Found architectures: $ARCHS"
# The CLI must be universal so it runs natively on Apple Silicon and Intel.
for arch in arm64 x86_64; do
if ! echo "$ARCHS" | grep -qw "$arch"; then
echo "❌ Error: $arch architecture missing from CLI binary"
exit 1
fi
done
echo "✅ CLI binary is universal (arm64 + x86_64)"
- name: Package App Bundle
run: |
cd build/Build/Products/Release
ditto -c -k --keepParent "${{ env.APP_NAME }}.app" "${{ env.APP_NAME }}.app.zip"
- name: Upload App Bundle
uses: actions/upload-artifact@v4
with:
name: ${{ env.APP_NAME }}-Unsigned
path: build/Build/Products/Release/${{ env.APP_NAME }}.app.zip
retention-days: 5
- name: Cache CLI Binary
uses: actions/cache/save@v4
with:
path: .cli-cache
key: trex-cli-${{ github.run_id }}-${{ github.run_attempt }}
# Validates Intel support (#69) by exercising the build on a real Intel runner.
# macos-15-intel is GitHub's last Intel (x86_64) image; the other macos-*
# images are Apple Silicon. (macos-13, the previous Intel image, was retired
# in December 2025.) The timeout fails fast if the Intel label stops being
# schedulable rather than hanging for the 6-hour default.
intel-smoke-test:
name: Intel (x86_64) Smoke Test
needs: build
runs-on: macos-15-intel
timeout-minutes: 20
steps:
- name: Confirm runner is Intel
run: |
ARCH=$(uname -m)
echo "Runner architecture: $ARCH"
if [ "$ARCH" != "x86_64" ]; then
echo "❌ Expected an Intel (x86_64) runner but got '$ARCH'."
echo "This job must run on Intel to validate Intel support (see #69)."
exit 1
fi
- name: Download Unsigned App
uses: actions/download-artifact@v4
with:
name: ${{ env.APP_NAME }}-Unsigned
path: .
- name: Restore CLI Binary from Cache
uses: actions/cache/restore@v4
with:
path: .cli-cache
key: trex-cli-${{ github.run_id }}-${{ github.run_attempt }}
fail-on-cache-miss: true
- name: Unpack App Bundle
run: ditto -x -k "${{ env.APP_NAME }}.app.zip" .
- name: Verify every embedded binary includes x86_64
run: |
APP="${{ env.APP_NAME }}.app"
FAIL=0
while IFS= read -r f; do
if file "$f" | grep -q "Mach-O"; then
ARCHS=$(lipo -archs "$f")
printf '%-60s %s\n' "${f#$APP/}" "$ARCHS"
echo "$ARCHS" | grep -qw x86_64 || { echo " ^^^ missing x86_64"; FAIL=1; }
fi
done < <(find "$APP" -type f \( -perm +111 -o -name "*.dylib" -o -name "*.framework" \) | sort)
if [ "$FAIL" != 0 ]; then
echo "❌ One or more embedded binaries lack an x86_64 slice (would crash on Intel)"
exit 1
fi
echo "✅ All embedded binaries include an x86_64 slice"
- name: Launch app on Intel
run: |
APP_BIN="${{ env.APP_NAME }}.app/Contents/MacOS/${{ env.APP_NAME }}"
"$APP_BIN" > app_launch.log 2>&1 &
APP_PID=$!
sleep 8
if kill -0 "$APP_PID" 2>/dev/null; then
echo "✅ App launched and is running natively on Intel"
kill "$APP_PID" 2>/dev/null || true
wait "$APP_PID" 2>/dev/null || true
else
wait "$APP_PID" 2>/dev/null; CODE=$?
echo "App process exited early (code $CODE). Output:"
cat app_launch.log || true
# A missing x86_64 slice makes the kernel reject the binary outright;
# that is the exact #69 failure and must fail the job. Other early
# exits (e.g. headless GUI quirks) are reported but not treated as
# an architecture failure.
if grep -qi "bad CPU type" app_launch.log; then
echo "❌ Kernel rejected the binary on Intel — x86_64 slice missing"
exit 1
fi
echo "⚠️ App did not stay running, but not due to a missing arch slice."
fi
- name: Run CLI on Intel
run: |
CLI=".cli-cache/trex"
chmod +x "$CLI"
echo "CLI architectures: $(lipo -archs "$CLI")"
# The runner is Intel, so a successful run proves the x86_64 slice
# executes natively (and that the bundled Tesseract/Leptonica x86_64
# native code loads).
if "$CLI" --help; then
echo "✅ CLI ran natively on Intel"
else
echo "❌ CLI failed to run on Intel"
exit 1
fi
sign:
needs: build
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Download Unsigned App
uses: actions/download-artifact@v4
with:
name: ${{ env.APP_NAME }}-Unsigned
path: .
- name: Restore CLI Binary from Cache
uses: actions/cache/restore@v4
with:
path: .cli-cache
key: trex-cli-${{ github.run_id }}-${{ github.run_attempt }}
fail-on-cache-miss: true
- name: Stage CLI Binary
run: |
CACHE_CLI_PATH=".cli-cache/trex"
if [ ! -f "$CACHE_CLI_PATH" ]; then
echo "Error: Cached CLI binary not found at $CACHE_CLI_PATH"
exit 1
fi
chmod +x "$CACHE_CLI_PATH"
- name: Unpack App Bundle
run: |
ditto -x -k "${{ env.APP_NAME }}.app.zip" .
- name: Normalize Embedded Frameworks
run: Scripts/flatten-frameworks.sh "${{ env.APP_NAME }}.app"
- name: Sign Application
uses: ./.github/actions/sign
with:
certificate: ${{ secrets.CERTIFICATES_P12 }}
certificate-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
apple-team-id: ${{ secrets.TEAM_ID }}
app-path: "${{ env.APP_NAME }}.app"
entitlements-path: "TRex/Resources/TRex.entitlements"
- name: Sign CLI Binary
uses: ./.github/actions/sign
with:
certificate: ${{ secrets.CERTIFICATES_P12 }}
certificate-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
apple-team-id: ${{ secrets.TEAM_ID }}
app-path: ".cli-cache/trex"
entitlements-path: ""
- name: Package Signed App Bundle
run: |
ditto -c -k --keepParent "${{ env.APP_NAME }}.app" "${{ env.APP_NAME }}.app.zip"
- name: Upload Signed App Bundle
uses: actions/upload-artifact@v4
with:
name: ${{ env.APP_NAME }}-Signed
path: ${{ env.APP_NAME }}.app.zip
retention-days: 5
- name: Cache Signed CLI Binary
uses: actions/cache/save@v4
with:
path: .cli-cache
key: trex-cli-signed-${{ github.run_id }}-${{ github.run_attempt }}
notarize:
needs: sign
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Download Signed App
uses: actions/download-artifact@v4
with:
name: ${{ env.APP_NAME }}-Signed
path: .
- name: Restore Signed CLI Binary from Cache
uses: actions/cache/restore@v4
with:
path: .cli-cache
key: trex-cli-signed-${{ github.run_id }}-${{ github.run_attempt }}
fail-on-cache-miss: true
- name: Stage CLI Binary
run: |
CACHE_CLI_PATH=".cli-cache/trex"
if [ ! -f "$CACHE_CLI_PATH" ]; then
echo "Error: Cached CLI binary not found at $CACHE_CLI_PATH"
exit 1
fi
chmod +x "$CACHE_CLI_PATH"
- name: Unpack App Bundle
run: |
ditto -x -k "${{ env.APP_NAME }}.app.zip" .
- name: Notarize Application
uses: ./.github/actions/notarize
with:
username: ${{ secrets.APPLE_ID }}
password: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
apple-team-id: ${{ secrets.TEAM_ID }}
app-path: "${{ env.APP_NAME }}.app"
- name: Notarize CLI Binary
run: |
# Create ZIP for notarization
ditto -c -k ".cli-cache/trex" trex.zip
# Submit for notarization and capture output
set +e # Don't exit on error, we'll handle it
xcrun notarytool submit \
trex.zip \
--wait \
--apple-id "${{ secrets.APPLE_ID }}" \
--password "${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}" \
--team-id "${{ secrets.TEAM_ID }}" \
--output-format json \
> notarization_output.json
NOTARIZE_EXIT_CODE=$?
set -e
# Display the output
cat notarization_output.json
# Check result using exit code and JSON parsing
if [ $NOTARIZE_EXIT_CODE -ne 0 ]; then
echo "❌ Notarization submission failed with exit code $NOTARIZE_EXIT_CODE"
# Try to get the submission ID for logs
SUBMISSION_ID=$(jq -r '.id // empty' notarization_output.json)
if [ -n "$SUBMISSION_ID" ]; then
echo "Fetching notarization logs for submission $SUBMISSION_ID..."
xcrun notarytool log "$SUBMISSION_ID" \
--apple-id "${{ secrets.APPLE_ID }}" \
--password "${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}" \
--team-id "${{ secrets.TEAM_ID }}" || true
fi
exit 1
fi
# Verify the status is "Accepted"
STATUS=$(jq -r '.status // empty' notarization_output.json)
if [ "$STATUS" != "Accepted" ]; then
echo "❌ Notarization failed with status: $STATUS"
exit 1
fi
echo "✅ CLI binary notarized successfully"
# Note: Stapling is not needed for command-line tools distributed via download
- name: Package Notarized App Bundle
run: |
ditto -c -k --keepParent "${{ env.APP_NAME }}.app" "${{ env.APP_NAME }}.app.zip"
- name: Upload Notarized App Bundle
uses: actions/upload-artifact@v4
with:
name: ${{ env.APP_NAME }}-Notarized
path: ${{ env.APP_NAME }}.app.zip
retention-days: 5
- name: Upload CLI Artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.APP_NAME }}-CLI
path: .cli-cache/trex
retention-days: 5
- name: Cache Notarized CLI Binary
uses: actions/cache/save@v4
with:
path: .cli-cache
key: trex-cli-notarized-${{ github.run_id }}-${{ github.run_attempt }}
release:
needs: [notarize, verify-release-notes, intel-smoke-test]
if: startsWith(github.ref, 'refs/tags/v')
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set version and beta variables
run: |
# Extract version and determine if beta
VERSION=$(echo "${{ github.ref_name }}" | sed 's/v//')
if echo "${{ github.ref_name }}" | grep -qi 'beta'; then
IS_BETA=true
else
IS_BETA=false
fi
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "IS_BETA=$IS_BETA" >> $GITHUB_ENV
echo "TAG=${{ github.ref_name }}" >> $GITHUB_ENV
echo "Debug: VERSION=$VERSION, IS_BETA=$IS_BETA, TAG=${{ github.ref_name }}"
- name: Download Notarized App
uses: actions/download-artifact@v4
with:
name: ${{ env.APP_NAME }}-Notarized
path: .
- name: Restore Notarized CLI Binary from Cache
uses: actions/cache/restore@v4
with:
path: .cli-cache
key: trex-cli-notarized-${{ github.run_id }}-${{ github.run_attempt }}
fail-on-cache-miss: true
- name: Stage CLI Binary
run: |
CACHE_CLI_PATH=".cli-cache/trex"
if [ ! -f "$CACHE_CLI_PATH" ]; then
echo "Error: Cached CLI binary not found at $CACHE_CLI_PATH"
exit 1
fi
chmod +x "$CACHE_CLI_PATH"
echo "Restored executable permissions for CLI binary"
- name: Generate CLI Checksum
run: |
shasum -a 256 .cli-cache/trex > trex.sha256
echo "Generated SHA-256 checksum for CLI binary"
cat trex.sha256
- name: Unpack App Bundle
run: |
ditto -x -k "${{ env.APP_NAME }}.app.zip" .
- name: Get Build Number
run: |
BUILD_NUMBER=$(defaults read "$(pwd)/${{ env.APP_NAME }}.app/Contents/Info" CFBundleVersion)
echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_ENV
echo "Debug: Retrieved BUILD_NUMBER=$BUILD_NUMBER from app bundle"
- name: Create Release Archive
run: |
ditto -c -k --keepParent "${{ env.APP_NAME }}.app" "${{ env.APP_NAME }}-${{ env.VERSION }}.zip"
- name: Calculate SHA256
run: |
SHA=$(shasum -a 256 "${{ env.APP_NAME }}-${{ env.VERSION }}.zip" | cut -d' ' -f1)
echo "ZIP_SHA=$SHA" >> $GITHUB_ENV
echo "Debug: Calculated ZIP_SHA=$SHA"
- name: Read Release Notes
id: release_notes
run: |
NOTES_PATH="docs/release-notes/${{ env.TAG }}.md"
if [ ! -f "$NOTES_PATH" ]; then
echo "Error: Release notes not found at $NOTES_PATH"
exit 1
fi
# Prepare escaped version for XML
CONTENT=$(cat "$NOTES_PATH" | perl -p -e 's/%/%25/g' | perl -p -e 's/\n/%0A/g' | perl -p -e 's/\r/%0D/g')
echo "CONTENT=$CONTENT" >> $GITHUB_OUTPUT
# Also store raw notes for release body and changelog
RELEASE_NOTES=$(cat "$NOTES_PATH" | awk '{printf "%s\\n", $0}')
echo "RELEASE_NOTES=$RELEASE_NOTES" >> $GITHUB_ENV
# Verify the content was set
if [ -z "$RELEASE_NOTES" ]; then
echo "Error: RELEASE_NOTES is empty"
exit 1
fi
- name: Generate Sparkle Signature
id: generate_signature
uses: ./.github/actions/sparkle-sign
with:
private-key: ${{ secrets.SPARKLE_PRIVATE_KEY }}
file-path: ${{ env.APP_NAME }}-${{ env.VERSION }}.zip
- name: Update Appcast
run: |
# Determine appcast file based on beta flag
APPCAST_NAME="appcast"
if [ "${IS_BETA}" = "true" ]; then
APPCAST_NAME="appcast_beta.xml"
else
APPCAST_NAME="appcast.xml"
fi
APPCAST_FILE="docs/${APPCAST_NAME}"
# Debug logs
echo "Updating appcast: $APPCAST_FILE"
echo "Using URL:" $( [ "${IS_BETA}" = "true" ] && echo "${BETA_FEED_URL}" || echo "${PROD_FEED_URL}" )
echo "Using signature: ${{ steps.generate_signature.outputs.signature }}"
echo "ZIP_SHA: ${ZIP_SHA}"
echo "BUILD_NUMBER: ${BUILD_NUMBER}"
echo "VERSION: ${VERSION}"
# Use unindented heredoc for proper XML formatting
cat > "$APPCAST_FILE" <<-EOF
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>${APP_NAME} $([ "${IS_BETA}" = "true" ] && echo "Beta " )Appcast</title>
<link>$([ "${IS_BETA}" = "true" ] && echo "${BETA_FEED_URL}" || echo "${PROD_FEED_URL}")</link>
<description>${APP_NAME} $([ "${IS_BETA}" = "true" ] && echo "Beta " )Updates</description>
<language>en</language>
<item>
<title>${APP_NAME} ${VERSION}</title>
<sparkle:version>${BUILD_NUMBER}</sparkle:version>
<sparkle:shortVersionString>${VERSION}</sparkle:shortVersionString>
<description><![CDATA[${CONTENT}]]></description>
<pubDate>$(date -R)</pubDate>
<enclosure
url="https://github.com/${{ github.repository }}/releases/download/${{ env.TAG }}/${APP_NAME}-${VERSION}.zip"
sparkle:version="${BUILD_NUMBER}"
sparkle:shortVersionString="${VERSION}"
type="application/octet-stream"
${{ steps.generate_signature.outputs.signature }}
sparkle:sha256="${ZIP_SHA}"
/>
</item>
</channel>
</rss>
EOF
# Validate the XML
xmllint --noout "$APPCAST_FILE" || { echo "Error: Invalid XML format in appcast"; exit 1; }
- name: Update CHANGELOG.md
run: |
TEMP_FILE=$(mktemp)
echo "# ${TAG} ($(date +'%Y-%m-%d'))" > "$TEMP_FILE"
echo "" >> "$TEMP_FILE"
cat "docs/release-notes/${TAG}.md" >> "$TEMP_FILE"
echo "" >> "$TEMP_FILE"
if [ -f "CHANGELOG.md" ]; then
cat "CHANGELOG.md" >> "$TEMP_FILE"
fi
mv "$TEMP_FILE" "CHANGELOG.md"
- name: Generate Homebrew Formula
if: ${{ env.IS_BETA != 'true' }}
run: |
# Only generate formula for non-beta releases
echo "Generating Homebrew formula for version $VERSION"
# Ensure Formula directory exists
mkdir -p Formula
# Create the formula from template
FORMULA_FILE="Formula/trex.rb"
cp docs/templates/trex.rb.template "$FORMULA_FILE"
# Replace template variables
sed -i '' "s/{{VERSION}}/$VERSION/g" "$FORMULA_FILE"
sed -i '' "s/{{SHA256}}/$ZIP_SHA/g" "$FORMULA_FILE"
echo "Generated Homebrew formula at $FORMULA_FILE"
- name: Commit and Push Changes
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
# Fetch latest main to avoid conflicts
git fetch origin main
# Preserve generated artifacts while switching branches
TMP_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_DIR"' EXIT
cp CHANGELOG.md "$TMP_DIR/CHANGELOG.md"
find docs -maxdepth 1 -name 'appcast*.xml' -exec cp {} "$TMP_DIR/" \;
# Preserve Formula if it exists (non-beta releases)
if [ "$IS_BETA" != "true" ] && [ -f "Formula/trex.rb" ]; then
cp Formula/trex.rb "$TMP_DIR/trex.rb"
fi
# Reset any local changes before checking out new branch
git reset --hard HEAD
git checkout -B release-docs-${TAG} origin/main
cp "$TMP_DIR/CHANGELOG.md" CHANGELOG.md
for FILE in "$TMP_DIR"/appcast*.xml; do
[ -e "$FILE" ] || continue
cp "$FILE" docs/
done
# Restore Formula if it was backed up
if [ -f "$TMP_DIR/trex.rb" ]; then
mkdir -p Formula
cp "$TMP_DIR/trex.rb" Formula/trex.rb
fi
git add CHANGELOG.md docs/appcast*.xml
if [ "$IS_BETA" != "true" ] && [ -f "Formula/trex.rb" ]; then
git add Formula/trex.rb
fi
git commit -m "docs: update CHANGELOG.md, appcast and Homebrew formula for ${TAG}" || echo "Nothing to commit"
git push origin release-docs-${TAG}:main
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: |
${{ env.APP_NAME }}-${{ env.VERSION }}.zip
.cli-cache/trex
trex.sha256
body_path: docs/release-notes/${{ env.TAG }}.md
prerelease: ${{ env.IS_BETA == 'true' }}
draft: false
fail_on_unmatched_files: true
generate_release_notes: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
notify:
needs: [build, sign, notarize]
if: always()
runs-on: ubuntu-latest
steps:
- name: Notify Status
run: |
echo "Build status: ${{ needs.build.result }}"
echo "Sign status: ${{ needs.sign.result }}"
echo "Notarize status: ${{ needs.notarize.result }}"