Skip to content

chore: bump to v0.1.2 #17

chore: bump to v0.1.2

chore: bump to v0.1.2 #17

Workflow file for this run

name: Release
on:
push:
tags:
- 'v*'
concurrency:
group: release
cancel-in-progress: false
permissions:
contents: write
env:
SCHEME: buddi
TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
SIGNING_IDENTITY: "Developer ID Application"
DEVELOPER_DIR: /Applications/Xcode_26.4.app/Contents/Developer
jobs:
release:
runs-on: macos-26
timeout-minutes: 45
steps:
- name: Generate App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.RELEASE_BOT_APP_ID }}
private-key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ steps.app-token.outputs.token }}
- name: Show Xcode version
run: |
echo "DEVELOPER_DIR=$DEVELOPER_DIR"
xcodebuild -version
xcrun swift --version
- name: Extract and validate version
id: version
run: |
VERSION=${GITHUB_REF_NAME#v}
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
BUILD_SETTINGS=$(xcodebuild -showBuildSettings \
-scheme $SCHEME 2>/dev/null)
MARKETING_VERSION=$(printf '%s\n' "$BUILD_SETTINGS" | awk -F' = ' '/MARKETING_VERSION/ { print $2; exit }')
BUILD_NUMBER=$(printf '%s\n' "$BUILD_SETTINGS" | awk -F' = ' '/CURRENT_PROJECT_VERSION/ { print $2; exit }')
if [ "$MARKETING_VERSION" != "$VERSION" ]; then
echo "::error::Tag v$VERSION does not match MARKETING_VERSION $MARKETING_VERSION"
exit 1
fi
LAST_BUILD=$(grep -oE '<sparkle:version>[0-9]+' Updates/appcast.xml 2>/dev/null | sed 's#<sparkle:version>##' | head -1 || true)
if [ -n "$LAST_BUILD" ] && [ "$BUILD_NUMBER" -le "$LAST_BUILD" ]; then
echo "::error::BUILD $BUILD_NUMBER must be > last published $LAST_BUILD"
exit 1
fi
echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_OUTPUT
echo "Validated: v$VERSION (build $BUILD_NUMBER)"
- name: Import code signing certificate
env:
CERTIFICATE_P12_BASE64: ${{ secrets.CERTIFICATE_P12_BASE64 }}
CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }}
run: |
KEYCHAIN=$RUNNER_TEMP/signing.keychain-db
KEYCHAIN_PW=$(openssl rand -base64 32)
security create-keychain -p "$KEYCHAIN_PW" "$KEYCHAIN"
security set-keychain-settings -lut 21600 "$KEYCHAIN"
security unlock-keychain -p "$KEYCHAIN_PW" "$KEYCHAIN"
echo "$CERTIFICATE_P12_BASE64" | base64 --decode > $RUNNER_TEMP/cert.p12
security import $RUNNER_TEMP/cert.p12 \
-P "$CERTIFICATE_PASSWORD" \
-A -t cert -f pkcs12 \
-k "$KEYCHAIN"
rm -f $RUNNER_TEMP/cert.p12
security set-key-partition-list \
-S apple-tool:,apple:,codesign: \
-s -k "$KEYCHAIN_PW" "$KEYCHAIN"
security list-keychain -d user -s "$KEYCHAIN"
- name: Resolve SPM dependencies
run: xcodebuild -resolvePackageDependencies -scheme $SCHEME
- name: Locate Sparkle tools
id: sparkle
run: |
SPARKLE_BIN=$(find ~/Library/Developer/Xcode/DerivedData \
-path "*/Sparkle/bin" -type d 2>/dev/null | head -1)
if [ -z "$SPARKLE_BIN" ]; then
echo "::error::Sparkle bin not found"
exit 1
fi
echo "SPARKLE_BIN=$SPARKLE_BIN" >> $GITHUB_OUTPUT
- name: Archive
run: |
rm -f mediaremote-adapter/MediaRemoteAdapterTestClient
xcodebuild archive \
-scheme $SCHEME \
-configuration Release \
-archivePath $RUNNER_TEMP/buddi.xcarchive \
DEVELOPMENT_TEAM=$TEAM_ID \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="$SIGNING_IDENTITY"
- name: Export app
run: |
/usr/libexec/PlistBuddy -c "Add :method string developer-id" $RUNNER_TEMP/export.plist
/usr/libexec/PlistBuddy -c "Add :teamID string $TEAM_ID" $RUNNER_TEMP/export.plist
xcodebuild -exportArchive \
-archivePath $RUNNER_TEMP/buddi.xcarchive \
-exportOptionsPlist $RUNNER_TEMP/export.plist \
-exportPath $RUNNER_TEMP/export
- name: Notarize and staple app
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
run: |
ditto -c -k --keepParent \
"$RUNNER_TEMP/export/buddi.app" \
"$RUNNER_TEMP/buddi-submit.zip"
xcrun notarytool submit "$RUNNER_TEMP/buddi-submit.zip" \
--apple-id "$APPLE_ID" \
--password "$APP_SPECIFIC_PASSWORD" \
--team-id "$TEAM_ID" \
--wait --timeout 15m
xcrun stapler staple "$RUNNER_TEMP/export/buddi.app"
- name: Create DMG
env:
VERSION: ${{ steps.version.outputs.VERSION }}
run: |
STAGING=$RUNNER_TEMP/dmg-staging
DMG_PATH=$RUNNER_TEMP/buddi-${VERSION}.dmg
mkdir -p "$STAGING"
cp -R "$RUNNER_TEMP/export/buddi.app" "$STAGING/"
ln -s /Applications "$STAGING/Applications"
hdiutil create \
-volname "Buddi $VERSION" \
-srcfolder "$STAGING" \
-ov -format UDZO \
"$DMG_PATH"
echo "DMG_PATH=$DMG_PATH" >> $GITHUB_ENV
- name: Write checksum
env:
VERSION: ${{ steps.version.outputs.VERSION }}
run: |
SHASUM=$(shasum -a 256 "$DMG_PATH" | awk '{print $1}')
printf '%s\n' "$SHASUM" > "$RUNNER_TEMP/buddi-${VERSION}.sha256"
{
echo "## Release checksum"
echo
echo "\`buddi-${VERSION}.dmg\` SHA-256:"
echo "\`$SHASUM\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Write Sparkle key
env:
SPARKLE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }}
run: |
echo "$SPARKLE_KEY" > "$RUNNER_TEMP/sparkle_key"
chmod 600 "$RUNNER_TEMP/sparkle_key"
- name: Sign DMG for Sparkle
env:
VERSION: ${{ steps.version.outputs.VERSION }}
SPARKLE_BIN: ${{ steps.sparkle.outputs.SPARKLE_BIN }}
run: |
"$SPARKLE_BIN/sign_update" --ed-key-file "$RUNNER_TEMP/sparkle_key" "$DMG_PATH"
- name: Update appcast.xml
env:
VERSION: ${{ steps.version.outputs.VERSION }}
SPARKLE_BIN: ${{ steps.sparkle.outputs.SPARKLE_BIN }}
run: |
mkdir -p $RUNNER_TEMP/appcast-staging
cp "$DMG_PATH" $RUNNER_TEMP/appcast-staging/
cp Updates/appcast.xml $RUNNER_TEMP/appcast-staging/ 2>/dev/null || true
"$SPARKLE_BIN/generate_appcast" \
--ed-key-file "$RUNNER_TEMP/sparkle_key" \
--download-url-prefix "https://github.com/talkvalue/Buddi/releases/download/v$VERSION/" \
-o Updates/appcast.xml \
$RUNNER_TEMP/appcast-staging
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
${{ env.DMG_PATH }}
${{ runner.temp }}/buddi-${{ steps.version.outputs.VERSION }}.sha256
- name: Commit updated appcast
env:
VERSION: ${{ steps.version.outputs.VERSION }}
run: |
cp Updates/appcast.xml /tmp/appcast.xml
git restore Updates/appcast.xml
git config user.name "buddi-release-bot[bot]"
git config user.email "buddi-release-bot[bot]@users.noreply.github.com"
git fetch origin main
git checkout main
git pull origin main
cp /tmp/appcast.xml Updates/appcast.xml
git add Updates/appcast.xml
git diff --cached --quiet && echo "No appcast changes" || \
git commit -m "chore: update appcast for v$VERSION"
git push origin main
- name: Deploy appcast to GitHub Pages
run: |
cp Updates/appcast.xml /tmp/appcast.xml
git checkout --orphan gh-pages-tmp
git rm -rf . --quiet
cp /tmp/appcast.xml appcast.xml
git add appcast.xml
git commit -m "Update appcast for v${{ steps.version.outputs.VERSION }}"
git push origin gh-pages-tmp:gh-pages --force
git checkout main
git branch -D gh-pages-tmp
- name: Update Homebrew cask
env:
VERSION: ${{ steps.version.outputs.VERSION }}
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
run: |
DMG_URL="https://github.com/talkvalue/Buddi/releases/download/v${VERSION}/buddi-${VERSION}.dmg"
for attempt in 1 2 3; do
if SHA256=$(curl -sL --fail "$DMG_URL" | shasum -a 256 | cut -d' ' -f1); then break; fi
echo "Attempt $attempt failed, retrying in 10s..."; sleep 10
done
[[ -n "${SHA256:-}" ]] || { echo "::error::Failed to download DMG for SHA256"; exit 1; }
git clone https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/talkvalue/homebrew-buddi.git $RUNNER_TEMP/tap
mkdir -p $RUNNER_TEMP/tap/Casks
cat > $RUNNER_TEMP/tap/Casks/buddi.rb << CASK
cask "buddi" do
version "${VERSION}"
sha256 "${SHA256}"
url "https://github.com/talkvalue/Buddi/releases/download/v#{version}/buddi-#{version}.dmg"
name "Buddi"
desc "Claude Code companion for the macOS notch"
homepage "https://github.com/talkvalue/Buddi"
livecheck do
url :url
strategy :github_latest
end
auto_updates true
depends_on macos: ">= :sequoia"
app "buddi.app"
uninstall quit: "com.talkvalue.buddi"
zap trash: [
"~/Library/Preferences/com.talkvalue.buddi.plist",
"~/Library/Application Support/Buddi",
]
end
CASK
cd $RUNNER_TEMP/tap
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Casks/buddi.rb
git diff --cached --quiet && echo "No cask changes" || \
git commit -m "Update Buddi to v${VERSION}" && \
git push "https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/talkvalue/homebrew-buddi.git" HEAD:main
- name: Cleanup secrets
if: always()
run: |
rm -f "$RUNNER_TEMP/sparkle_key"
security delete-keychain $RUNNER_TEMP/signing.keychain-db 2>/dev/null || true