Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@
"Bash(# Fix UIComponents imports to UI-Components\nfind . -name \"\"*.swift\"\" -type f | while read -r file; do\n if grep -q \"\"import UIComponents\"\" \"\"$file\"\"; then\n echo \"\"Fixing: $file\"\"\n sed -i '''' ''s/import UIComponents/import UI_Components/g'' \"\"$file\"\"\n fi\ndone)",
"mcp__filesystem__search_files",
"mcp__filesystem__edit_file",
"mcp__github__get_issue",
"Bash(git reset:*)",
"Bash(periphery scan:*)"
],
Expand Down
327 changes: 327 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
name: Deploy to App Store

on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
deployment_type:
description: 'Deployment Type'
required: true
default: 'testflight'
type: choice
options:
- testflight
- app-store
skip_tests:
description: 'Skip Tests'
required: false
default: false
type: boolean

env:
XCODE_VERSION: '15.0'
SWIFT_VERSION: '5.9'
BUNDLE_ID: 'com.homeinventory.app'
TEAM_ID: '2VXBQV4XC9'

jobs:
validate:
name: Validate Build Configuration
runs-on: macos-14
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ env.XCODE_VERSION }}

- name: Validate Swift Version
run: |
swift_version=$(swift --version | head -1 | awk '{print $4}')
echo "Swift version: $swift_version"

- name: Validate Bundle ID
run: |
bundle_id=$(xcodebuild -showBuildSettings -project ModularHomeInventory.xcodeproj -scheme ModularHomeInventory | grep PRODUCT_BUNDLE_IDENTIFIER | head -1 | awk '{print $3}')
if [ "$bundle_id" != "${{ env.BUNDLE_ID }}" ]; then
echo "Bundle ID mismatch: expected ${{ env.BUNDLE_ID }}, got $bundle_id"
exit 1
fi

- name: Check Certificates
env:
CERTIFICATE_PATH: ${{ runner.temp }}/build_certificate.p12
CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# Create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain

# Import certificate
echo "${{ secrets.BUILD_CERTIFICATE_BASE64 }}" | base64 --decode > $CERTIFICATE_PATH
security import $CERTIFICATE_PATH -P "$CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k build.keychain
security list-keychain -d user -s build.keychain

test:
name: Run Tests
runs-on: macos-14
needs: validate
if: ${{ !inputs.skip_tests }}
strategy:
matrix:
destination:
- platform=iOS Simulator,name=iPhone 15 Pro,OS=17.0
- platform=iOS Simulator,name=iPad Pro (12.9-inch) (6th generation),OS=17.0
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ env.XCODE_VERSION }}

- name: Cache SPM
uses: actions/cache@v3
with:
path: |
~/Library/Developer/Xcode/DerivedData/**/SourcePackages
~/Library/Caches/org.swift.swiftpm
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
restore-keys: |
${{ runner.os }}-spm-

- name: Resolve Dependencies
run: |
xcodebuild -resolvePackageDependencies \
-project ModularHomeInventory.xcodeproj \
-scheme ModularHomeInventory

- name: Run Tests
run: |
xcodebuild test \
-project ModularHomeInventory.xcodeproj \
-scheme ModularHomeInventory \
-destination '${{ matrix.destination }}' \
-enableCodeCoverage YES \
-resultBundlePath TestResults.xcresult \
| xcpretty --test --color

- name: Upload Test Results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results-${{ matrix.destination }}
path: TestResults.xcresult

- name: Generate Coverage Report
if: matrix.destination == 'platform=iOS Simulator,name=iPhone 15 Pro,OS=17.0'
run: |
xcov generate \
--project ModularHomeInventory.xcodeproj \
--scheme ModularHomeInventory \
--output_directory coverage \
--minimum_coverage_percentage 70

- name: Upload Coverage
if: matrix.destination == 'platform=iOS Simulator,name=iPhone 15 Pro,OS=17.0'
uses: codecov/codecov-action@v3
with:
directory: ./coverage
flags: unittests
name: codecov-umbrella

build:
name: Build Release
runs-on: macos-14
needs: [validate, test]
if: always() && needs.validate.result == 'success' && (needs.test.result == 'success' || needs.test.result == 'skipped')
outputs:
version: ${{ steps.version.outputs.version }}
build_number: ${{ steps.version.outputs.build_number }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ env.XCODE_VERSION }}

- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true

- name: Install Fastlane
run: |
gem install fastlane
bundle install

- name: Setup Certificates
env:
CERTIFICATE_PATH: ${{ runner.temp }}/build_certificate.p12
PP_PATH: ${{ runner.temp }}/build_pp.mobileprovision
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# Create keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -lut 21600 build.keychain

# Import certificate
echo "${{ secrets.BUILD_CERTIFICATE_BASE64 }}" | base64 --decode > $CERTIFICATE_PATH
security import $CERTIFICATE_PATH -P "${{ secrets.CERTIFICATE_PASSWORD }}" -A -t cert -f pkcs12 -k build.keychain
security list-keychain -d user -s build.keychain

# Apply provisioning profile
echo "${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}" | base64 --decode > $PP_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles

- name: Get Version Info
id: version
run: |
version=$(xcodebuild -showBuildSettings -project ModularHomeInventory.xcodeproj -scheme ModularHomeInventory | grep MARKETING_VERSION | head -1 | awk '{print $3}')
build_number=$(xcodebuild -showBuildSettings -project ModularHomeInventory.xcodeproj -scheme ModularHomeInventory | grep CURRENT_PROJECT_VERSION | head -1 | awk '{print $3}')
echo "version=$version" >> $GITHUB_OUTPUT
echo "build_number=$build_number" >> $GITHUB_OUTPUT

- name: Increment Build Number
if: inputs.deployment_type == 'testflight' || github.event_name == 'push'
run: |
fastlane run increment_build_number xcodeproj:"ModularHomeInventory.xcodeproj"

- name: Build Archive
run: |
xcodebuild archive \
-project ModularHomeInventory.xcodeproj \
-scheme ModularHomeInventory \
-configuration Release \
-archivePath $RUNNER_TEMP/ModularHomeInventory.xcarchive \
-allowProvisioningUpdates \
DEVELOPMENT_TEAM=${{ env.TEAM_ID }}

- name: Export IPA
run: |
xcodebuild -exportArchive \
-archivePath $RUNNER_TEMP/ModularHomeInventory.xcarchive \
-exportPath $RUNNER_TEMP/export \
-exportOptionsPlist ExportOptions.plist

- name: Upload IPA
uses: actions/upload-artifact@v3
with:
name: ModularHomeInventory.ipa
path: ${{ runner.temp }}/export/ModularHomeInventory.ipa

deploy:
name: Deploy to ${{ inputs.deployment_type || 'TestFlight' }}
runs-on: macos-14
needs: build
environment: ${{ inputs.deployment_type || 'testflight' }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Download IPA
uses: actions/download-artifact@v3
with:
name: ModularHomeInventory.ipa
path: ./build

- name: Setup App Store Connect API
env:
API_KEY: ${{ secrets.APPSTORE_API_KEY }}
API_KEY_ID: ${{ secrets.APPSTORE_API_KEY_ID }}
API_ISSUER_ID: ${{ secrets.APPSTORE_API_ISSUER_ID }}
run: |
mkdir -p ~/.appstoreconnect/private_keys
echo "$API_KEY" > ~/.appstoreconnect/private_keys/AuthKey_${API_KEY_ID}.p8

- name: Deploy to TestFlight
if: inputs.deployment_type == 'testflight' || github.event_name == 'push'
env:
API_KEY_ID: ${{ secrets.APPSTORE_API_KEY_ID }}
API_ISSUER_ID: ${{ secrets.APPSTORE_API_ISSUER_ID }}
run: |
xcrun altool --upload-app \
--type ios \
--file ./build/ModularHomeInventory.ipa \
--apiKey $API_KEY_ID \
--apiIssuer $API_ISSUER_ID

- name: Deploy to App Store
if: inputs.deployment_type == 'app-store'
run: |
fastlane deliver \
--ipa ./build/ModularHomeInventory.ipa \
--app_identifier ${{ env.BUNDLE_ID }} \
--team_id ${{ env.TEAM_ID }} \
--skip_screenshots \
--skip_metadata \
--submit_for_review \
--automatic_release \
--force

- name: Create GitHub Release
if: github.event_name == 'push'
uses: softprops/action-gh-release@v1
with:
files: ./build/ModularHomeInventory.ipa
body: |
## ModularHomeInventory v${{ needs.build.outputs.version }} (${{ needs.build.outputs.build_number }})

### What's New
- See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details

### Installation
- **TestFlight**: Available for beta testing
- **Direct Install**: Download the IPA below (requires proper provisioning)
draft: false
prerelease: ${{ inputs.deployment_type == 'testflight' }}

notify:
name: Send Notifications
runs-on: ubuntu-latest
needs: [build, deploy]
if: always()
steps:
- name: Send Slack Notification
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: |
Deployment Status: ${{ needs.deploy.result }}
Version: v${{ needs.build.outputs.version }} (${{ needs.build.outputs.build_number }})
Type: ${{ inputs.deployment_type || 'TestFlight' }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}

- name: Send Email Notification
if: needs.deploy.result == 'success'
uses: dawidd6/action-send-mail@v3
with:
server_address: smtp.gmail.com
server_port: 465
username: ${{ secrets.EMAIL_USERNAME }}
password: ${{ secrets.EMAIL_PASSWORD }}
subject: ModularHomeInventory Deployment Success
to: ${{ secrets.NOTIFICATION_EMAIL }}
from: GitHub Actions
body: |
ModularHomeInventory has been successfully deployed!

Version: v${{ needs.build.outputs.version }} (${{ needs.build.outputs.build_number }})
Deployment Type: ${{ inputs.deployment_type || 'TestFlight' }}

The build is now available for testing/release.
Loading