Skip to content

Build and Release

Build and Release #10

Workflow file for this run

name: Build and Release
on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
inputs:
tag:
description: "Tag for the release (e.g., v1.0.0)"
required: true
type: string
env:
SWIFT_VERSION: "6.1"
XCODE_VERSION: "16.2"
jobs:
build-and-release:
name: Build and Release
runs-on: macos-latest
permissions:
contents: write
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.2"
- name: Verify Swift version
run: swift --version
- name: Clean previous builds
run: swift package clean
- name: Resolve dependencies
run: swift package resolve
- name: Build in release mode
run: swift build -c release --arch arm64 --arch x86_64
- name: Create release directory
run: mkdir -p release
- name: Copy built executable
run: |
cp .build/apple/Products/Release/RainCheck release/
chmod +x release/RainCheck
- name: Get tag name
id: tag
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
else
echo "tag=${{ github.ref_name }}" >> $GITHUB_OUTPUT
fi
- name: Create tag if workflow_dispatch
if: github.event_name == 'workflow_dispatch'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "${{ steps.tag.outputs.tag }}" -m "Release ${{ steps.tag.outputs.tag }}"
git push origin "${{ steps.tag.outputs.tag }}"
- name: Create app bundle (optional for macOS distribution)
run: |
mkdir -p release/RainCheck.app/Contents/MacOS
mkdir -p release/RainCheck.app/Contents/Resources
cp release/RainCheck release/RainCheck.app/Contents/MacOS/
if [ -d Sources/RainCheck/Assets.xcassets/AppIcon.appiconset ]; then
mkdir -p AppIcon.iconset
cp Sources/RainCheck/Assets.xcassets/AppIcon.appiconset/icon_16x16.png AppIcon.iconset/icon_16x16.png
cp Sources/RainCheck/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png AppIcon.iconset/icon_16x16@2x.png
cp Sources/RainCheck/Assets.xcassets/AppIcon.appiconset/icon_32x32.png AppIcon.iconset/icon_32x32.png
cp Sources/RainCheck/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png AppIcon.iconset/icon_32x32@2x.png
cp Sources/RainCheck/Assets.xcassets/AppIcon.appiconset/icon_128x128.png AppIcon.iconset/icon_128x128.png
cp Sources/RainCheck/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png AppIcon.iconset/icon_128x128@2x.png
cp Sources/RainCheck/Assets.xcassets/AppIcon.appiconset/icon_256x256.png AppIcon.iconset/icon_256x256.png
cp Sources/RainCheck/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png AppIcon.iconset/icon_256x256@2x.png
cp Sources/RainCheck/Assets.xcassets/AppIcon.appiconset/icon_512x512.png AppIcon.iconset/icon_512x512.png
cp Sources/RainCheck/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png AppIcon.iconset/icon_512x512@2x.png
iconutil -c icns AppIcon.iconset -o release/RainCheck.app/Contents/Resources/AppIcon.icns
rm -rf AppIcon.iconset
elif [ -f Resources/AppIcon.icns ]; then
cp Resources/AppIcon.icns release/RainCheck.app/Contents/Resources/
elif [ -f AppIcon.icns ]; then
cp AppIcon.icns release/RainCheck.app/Contents/Resources/
fi
cat > release/RainCheck.app/Contents/Info.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>RainCheck</string>
<key>CFBundleExecutable</key>
<string>RainCheck</string>
<key>CFBundleIdentifier</key>
<string>com.github.andersfischernielsen.raincheck</string>
<key>CFBundleName</key>
<string>RainCheck</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${{ steps.tag.outputs.tag }}</string>
<key>CFBundleVersion</key>
<string>${{ steps.tag.outputs.tag }}</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>LSMinimumSystemVersion</key>
<string>13.0</string>
<key>LSUIElement</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2025. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
EOF
- name: Create ZIP archive
run: |
cd release
zip -r RainCheck-${{ steps.tag.outputs.tag }}-macOS.zip RainCheck.app
zip RainCheck-${{ steps.tag.outputs.tag }}-executable.zip RainCheck
cd ..
- name: Calculate checksums
run: |
cd release
shasum -a 256 *.zip > checksums.txt
cd ..
- name: Generate release notes
id: release_notes
run: |
TAG_NAME="${{ steps.tag.outputs.tag }}"
PREV_TAG=$(git tag --sort=-version:refname | grep -A 1 "$TAG_NAME" | tail -n 1)
if [ -n "$PREV_TAG" ] && [ "$PREV_TAG" != "$TAG_NAME" ]; then
echo "## What's Changed" > release_notes.md
echo "" >> release_notes.md
git log --pretty=format:"- %s (%h)" "$PREV_TAG".."$TAG_NAME" >> release_notes.md
echo "" >> release_notes.md
else
echo "## RainCheck ${TAG_NAME}" > release_notes.md
echo "" >> release_notes.md
echo "Initial release of RainCheck - a macOS menu bar app for rain forecasts along cycling routes." >> release_notes.md
echo "" >> release_notes.md
fi
echo "## Installation" >> release_notes.md
echo "" >> release_notes.md
echo "### App Bundle (Recommended)" >> release_notes.md
echo "1. Download \`RainCheck-${TAG_NAME}-macOS.zip\`" >> release_notes.md
echo "2. Extract the ZIP file" >> release_notes.md
echo "3. Move \`RainCheck.app\` to your Applications folder" >> release_notes.md
echo "4. Right-click the app and select \"Open\" (required for unsigned apps)" >> release_notes.md
echo "" >> release_notes.md
echo "### Command Line Executable" >> release_notes.md
echo "1. Download \`RainCheck-${TAG_NAME}-executable.zip\`" >> release_notes.md
echo "2. Extract the ZIP file" >> release_notes.md
echo "3. Run \`./RainCheck\` from the terminal" >> release_notes.md
echo "" >> release_notes.md
echo "## System Requirements" >> release_notes.md
echo "- macOS 13.0 or later" >> release_notes.md
echo "- Intel or Apple Silicon Mac" >> release_notes.md
echo "" >> release_notes.md
echo "## Checksums" >> release_notes.md
echo "\`\`\`" >> release_notes.md
cat release/checksums.txt >> release_notes.md
echo "\`\`\`" >> release_notes.md
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.tag.outputs.tag }}
name: RainCheck ${{ steps.tag.outputs.tag }}
body_path: release_notes.md
draft: false
prerelease: false
files: |
release/RainCheck-${{ steps.tag.outputs.tag }}-macOS.zip
release/RainCheck-${{ steps.tag.outputs.tag }}-executable.zip
release/checksums.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: RainCheck-${{ steps.tag.outputs.tag }}
path: |
release/
!release/RainCheck
retention-days: 30