Skip to content

Add GitHub Actions CI/CD pipelines #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 12, 2025
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
90 changes: 90 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# GitHub Actions Workflows

This directory contains automated workflows for the V2er-iOS project.

## Workflows

### 🔨 iOS Build and Test (`ios-build-test.yml`)
- **Trigger**: Push to main/develop, Pull requests to main
- **Purpose**: Build the app and run tests
- **Features**:
- Builds for iOS Simulator
- Runs unit and UI tests
- Caches Swift Package Manager dependencies
- Uploads test results on failure

### ✅ PR Validation (`pr-validation.yml`)
- **Trigger**: Pull request events
- **Purpose**: Validate pull requests before merge
- **Features**:
- SwiftLint analysis
- PR size labeling (XS, S, M, L, XL, XXL)
- Conventional commit message checking

### 🚀 Release (`release.yml`)
- **Trigger**: Manual workflow dispatch
- **Purpose**: Build and release to App Store
- **Features**:
- Version bumping (major/minor/patch)
- Archive and export IPA
- Upload to TestFlight
- Create GitHub release
- Option for TestFlight-only releases

### 📦 Dependency Updates (`dependency-update.yml`)
- **Trigger**: Weekly (Mondays at 9 AM UTC) or manual
- **Purpose**: Keep dependencies up to date
- **Features**:
- Updates Swift Package dependencies
- Creates automated pull request
- Security vulnerability scanning

### 📊 Code Quality (`code-quality.yml`)
- **Trigger**: Push to main/develop, Pull requests
- **Purpose**: Maintain code quality standards
- **Features**:
- SwiftFormat checking
- Code coverage reporting
- Coverage badge generation

## Required Secrets

To use these workflows, configure the following secrets in your repository:

### For Release Workflow:
- `CERTIFICATES_P12`: Base64 encoded distribution certificate
- `CERTIFICATES_PASSWORD`: Certificate password
- `KEYCHAIN_PASSWORD`: Temporary keychain password
- `PROVISIONING_PROFILE_BASE64`: Base64 encoded provisioning profile
- `TEAM_ID`: Apple Developer Team ID
- `APP_STORE_CONNECT_API_KEY_ID`: App Store Connect API key ID
- `APP_STORE_CONNECT_API_KEY_ISSUER_ID`: API key issuer ID
- `APP_STORE_CONNECT_API_KEY`: API key content

### For Coverage Badge (Optional):
- `GIST_SECRET`: GitHub personal access token with gist scope

## Setup Instructions

1. Generate required certificates and provisioning profiles from Apple Developer portal
2. Encode files to base64:
```bash
base64 -i certificate.p12 -o certificate_base64.txt
base64 -i profile.mobileprovision -o profile_base64.txt
```
3. Add secrets to repository settings
4. Update Xcode version in workflows if needed
5. Customize workflow triggers as needed

## Manual Triggers

Some workflows can be triggered manually from the Actions tab:
- **Release**: Choose release type (major/minor/patch) and TestFlight-only option
- **Dependency Updates**: Manually check for updates

## Maintenance

- Update Xcode version when new versions are released
- Review and update SwiftLint rules as needed
- Adjust test simulator versions for new iOS releases
- Monitor dependency update PRs for breaking changes
112 changes: 112 additions & 0 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: Code Quality

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]

jobs:
swiftformat:
name: SwiftFormat Check
runs-on: macos-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install SwiftFormat
run: brew install swiftformat

- name: Check code formatting
run: |
swiftformat --version
swiftformat . --lint --verbose
continue-on-error: true

- name: Generate format diff
if: failure()
run: |
swiftformat . --dryrun > format-diff.txt
cat format-diff.txt

- name: Upload format diff
if: failure()
uses: actions/upload-artifact@v4
with:
name: format-diff
path: format-diff.txt

code-coverage:
name: Code Coverage
runs-on: macos-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive

- name: Select Xcode version
run: sudo xcode-select -s /Applications/Xcode_15.4.app/Contents/Developer

- name: Install xcpretty
run: gem install xcpretty

- name: Build and test with coverage
run: |
xcodebuild test \
-project V2er.xcodeproj \
-scheme V2er \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 15' \
-enableCodeCoverage YES \
-derivedDataPath build/DerivedData \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO | xcpretty

- name: Generate coverage report
run: |
cd build/DerivedData
# Find the xcresult bundle
RESULT_BUNDLE=$(find . -name '*.xcresult' -type d | head -n 1)

if [ -z "$RESULT_BUNDLE" ]; then
echo "No test results found, setting coverage to 0%"
echo "coverage=0.00" >> $GITHUB_ENV
else
xcrun xccov view --report --json "$RESULT_BUNDLE" > coverage.json || echo '{}' > coverage.json

# Extract coverage percentage with fallback
COVERAGE=$(cat coverage.json | jq -r '.lineCoverage // 0' | awk '{printf "%.2f", $1 * 100}')
echo "Code coverage: ${COVERAGE}%"
echo "coverage=${COVERAGE}" >> $GITHUB_ENV
fi

- name: Create coverage badge
if: env.GIST_SECRET != ''
uses: schneegans/dynamic-badges-action@v1.6.0
with:
auth: ${{ secrets.GIST_SECRET }}
gistID: ${{ secrets.GIST_ID }}
filename: v2er-ios-coverage.json
label: Coverage
message: ${{ env.coverage }}%
color: ${{ env.coverage > 80 && 'success' || env.coverage > 60 && 'yellow' || 'critical' }}
env:
GIST_SECRET: ${{ secrets.GIST_SECRET }}

- name: Comment PR with coverage
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const coverage = parseFloat('${{ env.coverage }}');
const emoji = coverage > 80 ? '✅' : coverage > 60 ? '⚠️' : '❌';

github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## Code Coverage Report ${emoji}\n\nCurrent coverage: **${coverage}%**`
});
84 changes: 84 additions & 0 deletions .github/workflows/dependency-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Dependency Updates

on:
schedule:
# Run every Monday at 9 AM UTC
- cron: '0 9 * * 1'
workflow_dispatch:

jobs:
update-dependencies:
name: Update Swift Package Dependencies
runs-on: macos-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}

- name: Select Xcode version
run: sudo xcode-select -s /Applications/Xcode_15.4.app/Contents/Developer

- name: Update Swift packages
run: |
# Update all Swift package dependencies to latest versions
xcodebuild -project V2er.xcodeproj \
-scheme V2er \
-resolvePackageDependencies \
-scmProvider system

- name: Check for changes
id: git-check
run: |
git diff --exit-code || echo "changes=true" >> $GITHUB_OUTPUT

- name: Create Pull Request
if: steps.git-check.outputs.changes == 'true'
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore: update Swift package dependencies'
title: 'chore: update Swift package dependencies'
body: |
## Automated Dependency Update

This PR updates the Swift Package Manager dependencies to their latest compatible versions.

### Changes
- Updated Package.resolved with latest dependency versions

### Checklist
- [ ] Build passes with updated dependencies
- [ ] Tests pass with updated dependencies
- [ ] No breaking changes identified

Please review the dependency changes and ensure they don't introduce any breaking changes.

---
*This PR was automatically created by the dependency update workflow.*
branch: automated/dependency-updates
delete-branch: true
labels: |
dependencies
automated

check-vulnerabilities:
name: Security Vulnerability Check
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Run security audit
uses: actions/dependency-review-action@v3
with:
fail-on-severity: moderate

- name: Upload security report
uses: actions/upload-artifact@v4
if: failure()
with:
name: security-report
path: dependency-review-report.json
76 changes: 76 additions & 0 deletions .github/workflows/ios-build-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: iOS Build and Test

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
workflow_dispatch:

env:
DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer

jobs:
build-and-test:
name: Build and Test
runs-on: macos-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive

- name: Select Xcode version
run: sudo xcode-select -s /Applications/Xcode_15.4.app/Contents/Developer

- name: Show Xcode version
run: xcodebuild -version

- name: Install xcpretty
run: gem install xcpretty

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

- name: Resolve Swift packages
run: |
xcodebuild -resolvePackageDependencies \
-project V2er.xcodeproj \
-scheme V2er

- name: Build for testing
run: |
set -o pipefail && xcodebuild build-for-testing \
-project V2er.xcodeproj \
-scheme V2er \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 15' \
ONLY_ACTIVE_ARCH=YES \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO | xcpretty --color

- name: Run tests
run: |
set -o pipefail && xcodebuild test-without-building \
-project V2er.xcodeproj \
-scheme V2er \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 15' \
ONLY_ACTIVE_ARCH=YES \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO | xcpretty --color --test

- name: Upload test results
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-results
path: |
~/Library/Logs/DiagnosticReports/
~/Library/Developer/Xcode/DerivedData/**/Logs/Test/
Loading
Loading