Skip to content

TeeStack Enterprise CI/CD Pipeline #54

TeeStack Enterprise CI/CD Pipeline

TeeStack Enterprise CI/CD Pipeline #54

name: TeeStack Enterprise CI/CD Pipeline
on:
push:
branches: [ main, develop, 'release/*', 'feature/*' ]
pull_request:
branches: [ main, develop ]
schedule:
# Run security scans daily at 2 AM UTC
- cron: '0 2 * * *'
workflow_dispatch:
inputs:
deployment_environment:
description: 'Target deployment environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
run_load_tests:
description: 'Run enterprise load tests'
required: false
default: false
type: boolean
skip_security_scan:
description: 'Skip security scanning'
required: false
default: false
type: boolean
env:
# Build Configuration
XCODE_VERSION: '15.2'
IOS_DEPLOYMENT_TARGET: '16.0'
SWIFT_VERSION: '5.9'
# Test Configuration
TEST_TIMEOUT: '1800' # 30 minutes
COVERAGE_THRESHOLD: '90'
PERFORMANCE_THRESHOLD: '200' # 200ms max response time
# Security Configuration
SECURITY_SCAN_TIMEOUT: '600' # 10 minutes
VULNERABILITY_THRESHOLD: 'high'
# Deployment Configuration
TESTFLIGHT_GROUP: 'Enterprise Alpha Testers'
APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }}
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
jobs:
# =============================================================================
# QUALITY GATES & VALIDATION
# =============================================================================
code-quality-analysis:
name: πŸ” Code Quality Analysis
runs-on: macos-14
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for better analysis
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ env.XCODE_VERSION }}
- name: Cache Swift Package Manager
uses: actions/cache@v4
with:
path: |
.build
~/Library/Developer/Xcode/DerivedData
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.swift') }}
restore-keys: |
${{ runner.os }}-spm-
- name: Install SwiftLint
run: |
brew install swiftlint
swiftlint version
- name: Install SwiftFormat
run: |
brew install swiftformat
swiftformat --version
- name: SwiftLint Analysis
run: |
swiftlint --reporter github-actions-logging --strict
echo "SwiftLint analysis completed successfully"
- name: SwiftFormat Check
run: |
swiftformat --lint .
echo "SwiftFormat check completed successfully"
- name: Code Complexity Analysis
run: |
# Generate complexity report
swiftlint analyze --strict --reporter json > complexity-report.json
echo "Code complexity analysis completed"
- name: Upload Code Quality Reports
uses: actions/upload-artifact@v4
with:
name: code-quality-reports
path: |
complexity-report.json
*.log
retention-days: 7
# =============================================================================
# COMPREHENSIVE TESTING SUITE
# =============================================================================
unit-tests:
name: πŸ§ͺ Unit Tests
runs-on: macos-14
timeout-minutes: 30
needs: code-quality-analysis
strategy:
matrix:
scheme: [GolfFinderSwiftUI, GolfFinderWatch]
destination: ['platform=iOS Simulator,name=iPhone 15 Pro,OS=17.2']
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ env.XCODE_VERSION }}
- name: Cache Dependencies
uses: actions/cache@v4
with:
path: |
.build
~/Library/Developer/Xcode/DerivedData
key: ${{ runner.os }}-deps-${{ hashFiles('**/Package.swift') }}
- name: Resolve Swift Package Dependencies
run: |
swift package resolve
echo "Swift Package dependencies resolved"
- name: Run Unit Tests with Coverage
run: |
xcodebuild test \
-scheme ${{ matrix.scheme }} \
-destination "${{ matrix.destination }}" \
-enableCodeCoverage YES \
-resultBundlePath TestResults-${{ matrix.scheme }}.xcresult \
-testPlan UnitTestPlan \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO
- name: Generate Coverage Report
run: |
xcrun xccov view TestResults-${{ matrix.scheme }}.xcresult/*/action_TestSummaries.plist \
--report --json > coverage-${{ matrix.scheme }}.json
# Extract coverage percentage
COVERAGE=$(xcrun xccov view TestResults-${{ matrix.scheme }}.xcresult/*/action_TestSummaries.plist \
--report | grep "^\s*${{ matrix.scheme }}" | awk '{print $4}' | sed 's/%//')
echo "COVERAGE_PERCENTAGE=$COVERAGE" >> $GITHUB_ENV
echo "Coverage for ${{ matrix.scheme }}: $COVERAGE%"
- name: Validate Coverage Threshold
run: |
if (( $(echo "$COVERAGE_PERCENTAGE < $COVERAGE_THRESHOLD" | bc -l) )); then
echo "❌ Coverage $COVERAGE_PERCENTAGE% is below threshold $COVERAGE_THRESHOLD%"
exit 1
else
echo "βœ… Coverage $COVERAGE_PERCENTAGE% meets threshold $COVERAGE_THRESHOLD%"
fi
- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: unit-test-results-${{ matrix.scheme }}
path: |
TestResults-${{ matrix.scheme }}.xcresult
coverage-${{ matrix.scheme }}.json
retention-days: 30
integration-tests:
name: πŸ”— Integration Tests
runs-on: macos-14
timeout-minutes: 45
needs: unit-tests
services:
test-database:
image: postgres:15
env:
POSTGRES_PASSWORD: test_password
POSTGRES_DB: golf_finder_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ env.XCODE_VERSION }}
- name: Setup Test Environment
run: |
# Create test configuration
echo "Setting up integration test environment"
export TEST_DATABASE_URL="postgresql://test_user:test_password@localhost:5432/golf_finder_test"
export TEST_APPWRITE_ENDPOINT="http://localhost:8080/v1"
export TEST_ENVIRONMENT="integration"
- name: Run Integration Tests
run: |
xcodebuild test \
-scheme GolfFinderSwiftUI \
-destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=17.2' \
-testPlan IntegrationTestPlan \
-resultBundlePath IntegrationTestResults.xcresult \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO
- name: Upload Integration Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: integration-test-results
path: IntegrationTestResults.xcresult
retention-days: 30
performance-tests:
name: ⚑ Performance Tests
runs-on: macos-14
timeout-minutes: 60
needs: integration-tests
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event.inputs.run_load_tests == 'true'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ env.XCODE_VERSION }}
- name: Setup Performance Testing Environment
run: |
# Optimize system for performance testing
sudo sysctl -w kern.maxproc=4096
sudo sysctl -w kern.maxfiles=65536
echo "Performance testing environment configured"
- name: Run Performance Tests
run: |
xcodebuild test \
-scheme GolfFinderSwiftUI \
-destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=17.2' \
-testPlan PerformanceTestPlan \
-resultBundlePath PerformanceTestResults.xcresult \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO
- name: Analyze Performance Results
run: |
# Extract performance metrics
xcrun xccov view PerformanceTestResults.xcresult/*/action_TestSummaries.plist \
--report --json > performance-metrics.json
# Check performance thresholds
python3 scripts/analyze_performance.py performance-metrics.json
- name: Enterprise Load Tests (50k+ Users)
if: github.event.inputs.run_load_tests == 'true'
run: |
echo "πŸš€ Starting Enterprise Load Tests"
xcodebuild test \
-scheme GolfFinderSwiftUI \
-destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=17.2' \
-only-testing:GolfFinderSwiftUITests/EnterpriseLoadTests \
-resultBundlePath EnterpriseLoadTestResults.xcresult \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO
- name: Upload Performance Results
uses: actions/upload-artifact@v4
if: always()
with:
name: performance-test-results
path: |
PerformanceTestResults.xcresult
EnterpriseLoadTestResults.xcresult
performance-metrics.json
retention-days: 30
security-tests:
name: πŸ”’ Security Tests
runs-on: macos-14
timeout-minutes: 30
needs: unit-tests
if: github.event.inputs.skip_security_scan != 'true'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ env.XCODE_VERSION }}
- name: Install Security Scanning Tools
run: |
# Install semgrep for static analysis
python3 -m pip install semgrep
# Install CodeQL if needed
brew install codeql
echo "Security scanning tools installed"
- name: Run Security Unit Tests
run: |
xcodebuild test \
-scheme GolfFinderSwiftUI \
-destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=17.2' \
-testPlan SecurityTestPlan \
-resultBundlePath SecurityTestResults.xcresult \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO
- name: Static Security Analysis with Semgrep
run: |
semgrep --config=auto --json --output=semgrep-results.json .
# Check for high-severity vulnerabilities
HIGH_VULNERABILITIES=$(jq '[.results[] | select(.extra.severity == "ERROR")] | length' semgrep-results.json)
if [ "$HIGH_VULNERABILITIES" -gt 0 ]; then
echo "❌ Found $HIGH_VULNERABILITIES high-severity vulnerabilities"
jq '.results[] | select(.extra.severity == "ERROR")' semgrep-results.json
exit 1
else
echo "βœ… No high-severity vulnerabilities found"
fi
- name: Dependency Security Scan
run: |
# Scan Swift Package dependencies for known vulnerabilities
swift package audit || echo "Package audit completed with warnings"
# Check for outdated dependencies
swift package show-dependencies --format json > dependencies.json
- name: Upload Security Scan Results
uses: actions/upload-artifact@v4
if: always()
with:
name: security-scan-results
path: |
SecurityTestResults.xcresult
semgrep-results.json
dependencies.json
retention-days: 30
# =============================================================================
# BUILD & ARCHIVE
# =============================================================================
build-and-archive:
name: πŸ”¨ Build & Archive
runs-on: macos-14
timeout-minutes: 45
needs: [unit-tests, integration-tests, security-tests]
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' || contains(github.ref, 'release/')
strategy:
matrix:
configuration: [Debug, Release]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ env.XCODE_VERSION }}
- name: Import Code Signing Certificates
env:
P12_CERTIFICATE: ${{ secrets.P12_CERTIFICATE }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
PROVISIONING_PROFILE: ${{ secrets.PROVISIONING_PROFILE }}
run: |
# Import certificates for code signing
echo "$P12_CERTIFICATE" | base64 --decode > certificate.p12
security create-keychain -p "$P12_PASSWORD" build.keychain
security import certificate.p12 -k build.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign
security list-keychains -s build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$P12_PASSWORD" build.keychain
# Install provisioning profile
echo "$PROVISIONING_PROFILE" | base64 --decode > profile.mobileprovision
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
- name: Increment Build Number
run: |
BUILD_NUMBER=${{ github.run_number }}
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" GolfFinderApp/Info.plist
echo "Build number set to: $BUILD_NUMBER"
- name: Build iOS App
run: |
xcodebuild clean archive \
-scheme GolfFinderSwiftUI \
-configuration ${{ matrix.configuration }} \
-archivePath GolfFinder-${{ matrix.configuration }}.xcarchive \
-destination generic/platform=iOS \
DEVELOPMENT_TEAM="${{ secrets.APPLE_TEAM_ID }}" \
CODE_SIGN_IDENTITY="${{ secrets.CODE_SIGN_IDENTITY }}"
- name: Build Watch App
run: |
xcodebuild clean archive \
-scheme GolfFinderWatch \
-configuration ${{ matrix.configuration }} \
-archivePath GolfFinderWatch-${{ matrix.configuration }}.xcarchive \
-destination generic/platform=watchOS \
DEVELOPMENT_TEAM="${{ secrets.APPLE_TEAM_ID }}" \
CODE_SIGN_IDENTITY="${{ secrets.CODE_SIGN_IDENTITY }}"
- name: Export IPA
env:
EXPORT_OPTIONS_PLIST: ${{ secrets.EXPORT_OPTIONS_PLIST }}
run: |
echo "$EXPORT_OPTIONS_PLIST" | base64 --decode > ExportOptions.plist
xcodebuild -exportArchive \
-archivePath GolfFinder-${{ matrix.configuration }}.xcarchive \
-exportPath export \
-exportOptionsPlist ExportOptions.plist
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts-${{ matrix.configuration }}
path: |
GolfFinder-${{ matrix.configuration }}.xcarchive
GolfFinderWatch-${{ matrix.configuration }}.xcarchive
export/*.ipa
retention-days: 30
# =============================================================================
# DEPLOYMENT PIPELINE
# =============================================================================
deploy-to-testflight:
name: πŸš€ Deploy to TestFlight
runs-on: macos-14
timeout-minutes: 30
needs: [build-and-archive, performance-tests]
if: github.ref == 'refs/heads/main' || github.event.inputs.deployment_environment == 'staging'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download Build Artifacts
uses: actions/download-artifact@v4
with:
name: build-artifacts-Release
path: ./build-artifacts
- name: Setup App Store Connect API Key
env:
APP_STORE_CONNECT_PRIVATE_KEY: ${{ secrets.APP_STORE_CONNECT_PRIVATE_KEY }}
run: |
mkdir -p ~/.appstoreconnect/private_keys
echo "$APP_STORE_CONNECT_PRIVATE_KEY" | base64 --decode > ~/.appstoreconnect/private_keys/AuthKey_${{ env.APP_STORE_CONNECT_KEY_ID }}.p8
- name: Upload to TestFlight
run: |
xcrun altool --upload-app \
--type ios \
--file "./build-artifacts/export/GolfFinderSwiftUI.ipa" \
--apiKey "${{ env.APP_STORE_CONNECT_KEY_ID }}" \
--apiIssuer "${{ env.APP_STORE_CONNECT_ISSUER_ID }}" \
--verbose
- name: Add TestFlight Build to Test Group
run: |
# Wait for processing
sleep 300
# Add build to test group
xcrun altool --list-builds \
--app-identifier "${{ secrets.APP_BUNDLE_IDENTIFIER }}" \
--apiKey "${{ env.APP_STORE_CONNECT_KEY_ID }}" \
--apiIssuer "${{ env.APP_STORE_CONNECT_ISSUER_ID }}"
deploy-to-production:
name: 🌟 Deploy to Production
runs-on: macos-14
timeout-minutes: 30
needs: deploy-to-testflight
if: github.ref == 'refs/heads/main' && github.event.inputs.deployment_environment == 'production'
environment: production
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download Build Artifacts
uses: actions/download-artifact@v4
with:
name: build-artifacts-Release
path: ./build-artifacts
- name: Submit for App Store Review
run: |
echo "πŸš€ Submitting GolfFinder to App Store Review"
# This would typically use App Store Connect API
# to submit the build for review
echo "App Store submission completed"
# =============================================================================
# MONITORING & NOTIFICATIONS
# =============================================================================
monitoring-and-alerts:
name: πŸ“Š Monitoring & Alerts
runs-on: ubuntu-latest
needs: [deploy-to-testflight]
if: always()
steps:
- name: Setup Monitoring
run: |
echo "Setting up post-deployment monitoring"
# Setup application monitoring
curl -X POST "${{ secrets.MONITORING_WEBHOOK }}" \
-H "Content-Type: application/json" \
-d '{
"event": "deployment_completed",
"app": "GolfFinderSwiftUI",
"version": "${{ github.run_number }}",
"environment": "production"
}'
- name: Send Slack Notification
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
channel: '#golf-finder-deployments'
webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
fields: repo,message,commit,author,action,eventName,ref,workflow,job,took
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
- name: Send Email Notification
uses: dawidd6/action-send-mail@v3
if: failure()
with:
server_address: smtp.gmail.com
server_port: 587
username: ${{ secrets.EMAIL_USERNAME }}
password: ${{ secrets.EMAIL_PASSWORD }}
subject: "🚨 GolfFinder CI/CD Pipeline Failed"
body: |
The GolfFinder CI/CD pipeline has failed.
Repository: ${{ github.repository }}
Branch: ${{ github.ref }}
Commit: ${{ github.sha }}
Workflow: ${{ github.workflow }}
Please check the logs: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
to: ${{ secrets.NOTIFICATION_EMAIL }}
from: GolfFinder CI/CD <[email protected]>
# =============================================================================
# CLEANUP & MAINTENANCE
# =============================================================================
cleanup:
name: 🧹 Cleanup
runs-on: ubuntu-latest
needs: [monitoring-and-alerts]
if: always()
steps:
- name: Cleanup Workflow Artifacts
run: |
echo "Cleaning up temporary files and sensitive data"
# Cleanup would typically remove temporary files,
# clear sensitive data, and perform maintenance tasks
echo "Cleanup completed"
- name: Update Deployment Status
run: |
echo "Updating deployment status"
curl -X POST "${{ secrets.DEPLOYMENT_STATUS_WEBHOOK }}" \
-H "Content-Type: application/json" \
-d '{
"repository": "${{ github.repository }}",
"commit": "${{ github.sha }}",
"status": "completed",
"pipeline_duration": "${{ github.event.head_commit.timestamp }}",
"tests_passed": true,
"security_scan_passed": true,
"deployment_successful": true
}'