diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml deleted file mode 100644 index dcdb1a5..0000000 --- a/.github/workflows/pr-validation.yml +++ /dev/null @@ -1,418 +0,0 @@ -name: CI Validation - -on: - pull_request: - branches: [main, develop] - push: - branches: [main, develop] - -env: - DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer - # Set to 'true' to enable UI tests in CI (disabled by default due to flakiness) - RUN_UI_TESTS: false - -jobs: - validate: - name: Code Quality & Testing - runs-on: macos-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Detect repository type - id: detect - run: | - echo "Repository: $GITHUB_REPOSITORY" - echo "Repository name: ${GITHUB_REPOSITORY##*/}" - - if [[ "${GITHUB_REPOSITORY##*/}" == "SwiftProjectTemplate" ]]; then - echo "repo_type=template" >> $GITHUB_OUTPUT - echo "๐Ÿ”ง Detected SwiftProjectTemplate repository" - else - echo "repo_type=generated" >> $GITHUB_OUTPUT - echo "๐Ÿ“ฑ Detected generated project repository" - fi - - - name: Check if project is configured - id: check_configured - run: | - if [[ ! -f "project.yml" ]]; then - # Count commits (if this is a fresh template with minimal history, skip CI) - COMMIT_COUNT=$(git rev-list --count HEAD 2>/dev/null || echo "0") - echo "Commit count: $COMMIT_COUNT" - - if [[ $COMMIT_COUNT -le 2 ]]; then - echo "โญ๏ธ Skipping CI - this appears to be an unconfigured template repository" - echo "๐Ÿ‘‰ Run './scripts/setup.sh' to configure your project, then commit to enable CI" - echo "configured=false" >> $GITHUB_OUTPUT - echo "should_skip=true" >> $GITHUB_OUTPUT - else - echo "โŒ Missing project.yml but git history exists - please run './scripts/setup.sh'" - echo "configured=false" >> $GITHUB_OUTPUT - echo "should_skip=false" >> $GITHUB_OUTPUT - exit 1 - fi - else - echo "โœ… Project appears to be configured (project.yml found)" - echo "configured=true" >> $GITHUB_OUTPUT - echo "should_skip=false" >> $GITHUB_OUTPUT - fi - - - name: Set up Xcode - if: steps.check_configured.outputs.should_skip != 'true' - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: "16.4" - - - name: Cache Homebrew packages - if: steps.check_configured.outputs.should_skip != 'true' - uses: actions/cache@v4 - with: - path: | - ~/Library/Caches/Homebrew - /usr/local/Homebrew - key: ${{ runner.os }}-homebrew-${{ hashFiles('**/Brewfile') }} - restore-keys: | - ${{ runner.os }}-homebrew- - - - name: Install dependencies - if: steps.check_configured.outputs.should_skip != 'true' - run: | - # Install Homebrew dependencies - brew bundle install --file=./Brewfile - - # Verify installations - echo "Installed versions:" - swiftlint --version - swiftformat --version - xcodegen --version - yq --version - - # Template Repository: Generate test project for E2E validation - - name: Generate test project - if: steps.detect.outputs.repo_type == 'template' && steps.check_configured.outputs.should_skip != 'true' - run: | - echo "๐Ÿ—๏ธ Generating test project for E2E validation..." - echo "Working directory: $(pwd)" - echo "GITHUB_WORKSPACE: $GITHUB_WORKSPACE" - - # Create test directory but run setup from template root - mkdir -p ci-test-project - ./scripts/dev-sync.sh to ci-test-project - - echo "Running setup script from template root directory" - echo "Target directory: $(pwd)/ci-test-project" - - set -x # Enable verbose output - cd ci-test-project - - # Run setup script from template root, but in the ci-test-project context - ./scripts/setup.sh \ - --project-name "CITestApp" \ - --bundle-id-root "com.ci.test" \ - --deployment-target 18.0 \ - --swift-version 5.10 \ - --test-framework "swift-testing" \ - --no-git-hooks \ - --public \ - --skip-brew \ - --force || { - echo "Setup script failed with exit code $?" - echo "Contents of current directory:" - ls -la - echo "Contents of parent directory:" - ls -la .. - exit 1 - } - set +x # Disable verbose output - - echo "โœ“ Test project generated successfully" - echo "Generated files:" - ls -la - - # Template Repository: Validate generated project - - name: Run E2E validation on generated project - if: steps.detect.outputs.repo_type == 'template' && steps.check_configured.outputs.should_skip != 'true' - run: | - echo "๐Ÿ” Running end-to-end validation on generated project..." - cd ci-test-project - - # Run the generated project's preflight check - echo "Running preflight check..." - ./scripts/preflight.sh - - echo "โœ“ E2E validation completed successfully" - - # Generated Project: Normal validation continues below - - name: Validate project configuration - if: steps.detect.outputs.repo_type == 'generated' && steps.check_configured.outputs.should_skip != 'true' - run: | - # Check if project.yml exists and is valid - if [ -f "project.yml" ]; then - echo "โœ“ project.yml found" - yq eval '.' project.yml > /dev/null - echo "โœ“ project.yml is valid YAML" - - # Extract project name for validation - PROJECT_NAME=$(yq eval '.name' project.yml) - echo "โœ“ Project name: $PROJECT_NAME" - - # Validate deployment target - DEPLOYMENT_TARGET=$(yq eval '.options.deploymentTarget.iOS' project.yml) - echo "โœ“ iOS Deployment Target: $DEPLOYMENT_TARGET" - else - echo "โœ— project.yml not found in generated project" - exit 1 - fi - - - name: Generate Xcode project - if: steps.detect.outputs.repo_type == 'generated' && steps.check_configured.outputs.should_skip != 'true' - run: | - echo "Generating Xcode project..." - xcodegen generate - - PROJECT_NAME=$(yq eval '.name' project.yml) - if [ -d "${PROJECT_NAME}.xcodeproj" ]; then - echo "โœ“ Xcode project generated successfully" - else - echo "โœ— Failed to generate Xcode project" - exit 1 - fi - - - name: SwiftLint - if: steps.detect.outputs.repo_type == 'generated' && steps.check_configured.outputs.should_skip != 'true' - run: | - echo "Running SwiftLint..." - - # Get project name and check if source directories exist - PROJECT_NAME=$(yq eval '.name' project.yml) - - if [ -d "$PROJECT_NAME" ]; then - swiftlint lint --strict - echo "โœ“ SwiftLint passed" - else - echo "โš  Source directory $PROJECT_NAME not found, skipping SwiftLint" - fi - - - name: SwiftFormat check - if: steps.detect.outputs.repo_type == 'generated' && steps.check_configured.outputs.should_skip != 'true' - run: | - echo "Checking SwiftFormat..." - - PROJECT_NAME=$(yq eval '.name' project.yml) - - if [ -d "$PROJECT_NAME" ]; then - # Check if code is properly formatted - swiftformat --lint $PROJECT_NAME - echo "โœ“ SwiftFormat check passed" - else - echo "โš  Source directory $PROJECT_NAME not found, skipping SwiftFormat" - fi - - - name: Build project - if: steps.detect.outputs.repo_type == 'generated' && steps.check_configured.outputs.should_skip != 'true' - run: | - echo "Building project..." - - PROJECT_NAME=$(yq eval '.name' project.yml) - DEPLOYMENT_TARGET=$(yq eval '.options.deploymentTarget.iOS' project.yml) - - # Determine simulator based on deployment target - if [ "$DEPLOYMENT_TARGET" != "null" ] && [ -n "$DEPLOYMENT_TARGET" ]; then - MAJOR_VERSION=$(echo $DEPLOYMENT_TARGET | cut -d'.' -f1) - if [ "$MAJOR_VERSION" -ge "17" ]; then - SIMULATOR_NAME="iPhone 16 Pro" - else - SIMULATOR_NAME="iPhone 15 Pro" - fi - else - SIMULATOR_NAME="iPhone 16 Pro" - fi - - echo "Building for $SIMULATOR_NAME..." - - xcodebuild build \ - -project "${PROJECT_NAME}.xcodeproj" \ - -scheme "$PROJECT_NAME" \ - -destination "platform=iOS Simulator,name=$SIMULATOR_NAME" \ - -configuration Debug \ - ONLY_ACTIVE_ARCH=YES \ - CODE_SIGNING_REQUIRED=NO \ - CODE_SIGNING_ALLOWED=NO - - echo "โœ“ Build completed successfully" - - - name: Run unit tests - if: steps.detect.outputs.repo_type == 'generated' && steps.check_configured.outputs.should_skip != 'true' - run: | - PROJECT_NAME=$(yq eval '.name' project.yml) - - # Check if test target exists - HAS_TESTS=$(yq eval ".targets | has(\"${PROJECT_NAME}Tests\")" project.yml) - - if [ "$HAS_TESTS" = "true" ]; then - echo "Running unit tests..." - - DEPLOYMENT_TARGET=$(yq eval '.options.deploymentTarget.iOS' project.yml) - if [ "$DEPLOYMENT_TARGET" != "null" ] && [ -n "$DEPLOYMENT_TARGET" ]; then - MAJOR_VERSION=$(echo $DEPLOYMENT_TARGET | cut -d'.' -f1) - if [ "$MAJOR_VERSION" -ge "17" ]; then - SIMULATOR_NAME="iPhone 16 Pro" - else - SIMULATOR_NAME="iPhone 15 Pro" - fi - else - SIMULATOR_NAME="iPhone 16 Pro" - fi - - xcodebuild test \ - -project "${PROJECT_NAME}.xcodeproj" \ - -scheme "$PROJECT_NAME" \ - -destination "platform=iOS Simulator,name=$SIMULATOR_NAME" \ - -configuration Debug \ - -enableCodeCoverage YES \ - ONLY_ACTIVE_ARCH=YES \ - CODE_SIGNING_REQUIRED=NO \ - CODE_SIGNING_ALLOWED=NO - - echo "โœ“ Unit tests completed" - else - echo "โš  No unit test target found, skipping tests" - fi - - # UI Tests (Optional - disabled by default) - # To enable UI tests in CI, set RUN_UI_TESTS: true in the env section above - - name: Run UI tests - if: steps.detect.outputs.repo_type == 'generated' && env.RUN_UI_TESTS == 'true' && steps.check_configured.outputs.should_skip != 'true' - run: | - PROJECT_NAME=$(yq eval '.name' project.yml) - - # Check if UI test target exists - HAS_UI_TESTS=$(yq eval ".targets | has(\"${PROJECT_NAME}UITests\")" project.yml) - - if [ "$HAS_UI_TESTS" = "true" ]; then - echo "Running UI tests..." - - DEPLOYMENT_TARGET=$(yq eval '.options.deploymentTarget.iOS' project.yml) - if [ "$DEPLOYMENT_TARGET" != "null" ] && [ -n "$DEPLOYMENT_TARGET" ]; then - MAJOR_VERSION=$(echo $DEPLOYMENT_TARGET | cut -d'.' -f1) - if [ "$MAJOR_VERSION" -ge "17" ]; then - SIMULATOR_NAME="iPhone 16 Pro" - else - SIMULATOR_NAME="iPhone 15 Pro" - fi - else - SIMULATOR_NAME="iPhone 16 Pro" - fi - - # UI tests often need more time and resources - xcodebuild test \ - -project "${PROJECT_NAME}.xcodeproj" \ - -scheme "$PROJECT_NAME" \ - -destination "platform=iOS Simulator,name=$SIMULATOR_NAME" \ - -configuration Debug \ - -only-testing:"${PROJECT_NAME}UITests" \ - ONLY_ACTIVE_ARCH=YES \ - CODE_SIGNING_REQUIRED=NO \ - CODE_SIGNING_ALLOWED=NO || { - echo "โš  UI tests failed, but continuing (UI tests can be flaky in CI)" - # Don't fail the build for UI test failures in CI - } - - echo "โœ“ UI tests completed" - else - echo "โš  No UI test target found, skipping UI tests" - fi - - - name: Validate scripts - if: steps.detect.outputs.repo_type == 'generated' && steps.check_configured.outputs.should_skip != 'true' - run: | - echo "Validating helper scripts..." - - # Check that all scripts are executable - find scripts -name "*.sh" -type f | while read script; do - if [ -x "$script" ]; then - echo "โœ“ $script is executable" - else - echo "โœ— $script is not executable" - exit 1 - fi - done - - # Check script syntax - find scripts -name "*.sh" -type f | while read script; do - if bash -n "$script"; then - echo "โœ“ $script syntax is valid" - else - echo "โœ— $script has syntax errors" - exit 1 - fi - done - - echo "โœ“ All scripts validated" - - - name: Check configuration files - if: steps.detect.outputs.repo_type == 'generated' && steps.check_configured.outputs.should_skip != 'true' - run: | - echo "Validating configuration files..." - - # Check .swiftlint.yml if it exists - if [ -f ".swiftlint.yml" ]; then - yq eval '.' .swiftlint.yml > /dev/null - echo "โœ“ .swiftlint.yml is valid" - fi - - # Check .swiftformat if it exists - if [ -f ".swiftformat" ]; then - echo "โœ“ .swiftformat found" - fi - - # Check simulator.yml if it exists - if [ -f "simulator.yml" ]; then - yq eval '.' simulator.yml > /dev/null - echo "โœ“ simulator.yml is valid" - fi - - # Check Brewfile - if [ -f "Brewfile" ]; then - echo "โœ“ Brewfile found" - fi - - echo "โœ“ Configuration files validated" - - - name: Summary - if: steps.detect.outputs.repo_type == 'generated' && steps.check_configured.outputs.should_skip != 'true' - run: | - echo "๐ŸŽ‰ PR Validation Complete!" - echo "" - echo "โœ… Code quality checks passed" - echo "โœ… Build completed successfully" - echo "โœ… Tests executed" - echo "โœ… Configuration validated" - echo "" - echo "This PR is ready for review! ๐Ÿš€" - - # Template Repository: Cleanup - - name: Cleanup test project - if: always() && steps.detect.outputs.repo_type == 'template' && steps.check_configured.outputs.should_skip != 'true' - run: | - echo "๐Ÿงน Cleaning up test project..." - rm -rf ci-test-project - echo "โœ“ Cleanup completed" - - # Show helpful message when CI is skipped - - name: Configuration needed - if: steps.check_configured.outputs.should_skip == 'true' - run: | - echo "๐Ÿš€ Welcome to your new iOS project!" - echo "" - echo "This appears to be a fresh project created from the SwiftProjectTemplate." - echo "To enable CI and configure your project, please run:" - echo "" - echo " ./scripts/setup.sh" - echo "" - echo "After running setup and committing your changes, CI will automatically" - echo "run on future pushes and pull requests." - echo "" - echo "๐Ÿ“– Check the README.md for detailed setup instructions!" diff --git a/.github/workflows/template-validation.yml b/.github/workflows/template-validation.yml new file mode 100644 index 0000000..5baf2e0 --- /dev/null +++ b/.github/workflows/template-validation.yml @@ -0,0 +1,132 @@ +name: Template Validation + +on: + pull_request: + branches: [main, develop] + push: + branches: [main, develop] + +env: + DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer + +jobs: + validate-template: + name: Template End-to-End Validation + runs-on: macos-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: "16.0" + + - name: Cache Homebrew packages + uses: actions/cache@v4 + with: + path: | + ~/Library/Caches/Homebrew + /usr/local/Homebrew + key: ${{ runner.os }}-homebrew-${{ hashFiles('**/Brewfile') }} + restore-keys: | + ${{ runner.os }}-homebrew- + + - name: Install dependencies + run: | + # Install Homebrew dependencies + brew bundle install --file=./Brewfile + + # Verify installations + echo "Installed versions:" + swiftlint --version + swiftformat --version + xcodegen --version + yq --version + + - name: Generate test project + run: | + echo "๐Ÿ—๏ธ Generating test project for E2E validation..." + echo "Working directory: $(pwd)" + echo "GITHUB_WORKSPACE: $GITHUB_WORKSPACE" + + # Create test directory but run setup from template root + mkdir -p ci-test-project + ./scripts/dev-sync.sh to ci-test-project + + echo "Running setup script from template root directory" + echo "Target directory: $(pwd)/ci-test-project" + + set -x # Enable verbose output + cd ci-test-project + + echo "๐Ÿ” Debugging setup script execution..." + echo "Current directory: $(pwd)" + echo "Setup script location: $(ls -la scripts/setup.sh)" + echo "Setup script is executable: $(test -x scripts/setup.sh && echo 'YES' || echo 'NO')" + + # Check if all required tools are available + echo "๐Ÿ”ง Checking tool availability..." + which bash || echo "bash not found" + bash --version || echo "bash version check failed" + + # Run setup script with bash debugging and verbose output + echo "๐Ÿš€ Running setup script with full debugging..." + bash -x ./scripts/setup.sh \ + --project-name "CITestApp" \ + --bundle-id-root "com.ci.test" \ + --deployment-target 18.0 \ + --swift-version 5.10 \ + --test-framework "swift-testing" \ + --source-language en \ + --no-git-hooks \ + --no-commit \ + --public \ + --skip-brew \ + --force 2>&1 || { + echo "โŒ Setup script failed with exit code $?" + echo "" + echo "๐Ÿ“ Contents of current directory:" + ls -la + echo "" + echo "๐Ÿ“ Contents of parent directory:" + ls -la .. + echo "" + echo "๐Ÿ“ Setup script contents (first 50 lines):" + head -50 scripts/setup.sh + exit 1 + } + set +x # Disable verbose output + + echo "โœ“ Test project generated successfully" + echo "Generated files:" + ls -la + + - name: Run E2E validation on generated project + run: | + echo "๐Ÿ” Running end-to-end validation on generated project..." + cd ci-test-project + + # Run the generated project's preflight check + echo "Running preflight check..." + ./scripts/preflight.sh + + echo "โœ“ E2E validation completed successfully" + + - name: Cleanup test project + if: always() + run: | + echo "๐Ÿงน Cleaning up test project..." + rm -rf ci-test-project + echo "โœ“ Cleanup completed" + + - name: Template validation summary + run: | + echo "๐ŸŽ‰ Template Validation Complete!" + echo "" + echo "โœ… Template generates project successfully" + echo "โœ… Generated project passes all quality checks" + echo "โœ… E2E workflow validated" + echo "" + echo "Template is ready for use! ๐Ÿš€" diff --git a/scripts/dev-sync.sh b/scripts/dev-sync.sh index bfb9e98..9ea0f2f 100755 --- a/scripts/dev-sync.sh +++ b/scripts/dev-sync.sh @@ -39,7 +39,7 @@ EXAMPLES: $0 diff ../MyTestApp SYNCABLE ITEMS: - โ€ข scripts/, templates/, .github/, .vscode/ + โ€ข scripts/, templates/, .github/ (excluding template-validation.yml), .vscode/ โ€ข Brewfile, .swift-version, .markdownlint.json EOF @@ -134,12 +134,18 @@ sync_to_project() { read -ra rsync_cmd <<< "$(build_rsync_cmd)" # Sync directories - for dir in scripts templates .github .vscode; do + for dir in scripts templates .vscode; do if [[ -d "$dir" ]]; then "${rsync_cmd[@]}" "$dir/" "$TARGET_PROJECT/$dir/" fi done + # Sync .github directory but exclude template-specific workflows + if [[ -d ".github" ]]; then + # Only sync non-template workflows and other .github files + "${rsync_cmd[@]}" --exclude="workflows/template-validation.yml" .github/ "$TARGET_PROJECT/.github/" + fi + # Sync individual files for file in Brewfile .gitignore .swift-version .markdownlint.json; do if [[ -f "$file" ]]; then @@ -157,12 +163,18 @@ sync_from_project() { read -ra rsync_cmd <<< "$(build_rsync_cmd)" # Sync directories (reverse direction) - for dir in scripts templates .github .vscode; do + for dir in scripts templates .vscode; do if [[ -d "$TARGET_PROJECT/$dir" ]]; then "${rsync_cmd[@]}" "$TARGET_PROJECT/$dir/" "$dir/" fi done + # Sync .github directory but exclude template-specific workflows (reverse direction) + if [[ -d "$TARGET_PROJECT/.github" ]]; then + # Only sync back non-template workflows and other .github files + "${rsync_cmd[@]}" --exclude="workflows/template-validation.yml" "$TARGET_PROJECT/.github/" .github/ + fi + # Sync individual files (reverse direction) for file in Brewfile .gitignore .swift-version .markdownlint.json; do if [[ -f "$TARGET_PROJECT/$file" ]]; then @@ -177,7 +189,7 @@ show_diff() { log_info "Showing differences between template and project: $TARGET_PROJECT" # Use rsync --dry-run to show what would be copied - rsync -avcn --delete scripts/ templates/ .github/ .vscode/ Brewfile .swift-version .markdownlint.json "$TARGET_PROJECT/" 2>/dev/null || { + rsync -avcn --delete --exclude="workflows/template-validation.yml" scripts/ templates/ .github/ .vscode/ Brewfile .swift-version .markdownlint.json "$TARGET_PROJECT/" 2>/dev/null || { log_info "Run with 'to' mode to see what files would be synced" } } diff --git a/scripts/setup.sh b/scripts/setup.sh index cc52cf5..5916696 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -12,8 +12,8 @@ source "$(dirname "${BASH_SOURCE[0]}")/_helpers.sh" # Default values PROJECT_NAME="" -DEPLOYMENT_TARGET="26.0" -SWIFT_VERSION="6.2" +DEPLOYMENT_TARGET="18.0" +SWIFT_VERSION="5.10" PROJECT_TYPE="private" # private or public BUNDLE_ID_ROOT="com.yourcompany" TEST_FRAMEWORK="swift-testing" # swift-testing or xctest @@ -314,7 +314,7 @@ prompt_for_missing_info() { DEPLOYMENT_TARGET="$input_deployment_target" if ! validate_ios_version "$DEPLOYMENT_TARGET"; then log_error "Invalid deployment target format. Using default: $DEPLOYMENT_TARGET" - DEPLOYMENT_TARGET="26.0" + DEPLOYMENT_TARGET="18.0" fi fi fi @@ -327,7 +327,7 @@ prompt_for_missing_info() { SWIFT_VERSION="$input_swift_version" if ! validate_swift_version "$SWIFT_VERSION"; then log_error "Invalid Swift version format. Using default: $SWIFT_VERSION" - SWIFT_VERSION="6.2" + SWIFT_VERSION="5.10" fi fi fi @@ -493,6 +493,7 @@ generate_template_files() { ".swiftlint.yml.template:.swiftlint.yml" ".swiftformat.template:.swiftformat" "README.md.template:README.md" + "ci.yml.template:.github/workflows/ci.yml" ) for file_mapping in "${files_to_generate[@]}"; do @@ -531,6 +532,14 @@ generate_template_files() { # Generate file from template if [[ -f "$template_path" ]]; then + # Create output directory if needed + local output_dir + output_dir=$(dirname "$output_path") + if [[ ! -d "$output_dir" ]]; then + mkdir -p "$output_dir" + log_info "Created directory: $output_dir" + fi + replace_template_vars "$template_path" "$output_path" "${template_vars[@]}" log_success "Generated $output_file" else diff --git a/templates/ci.yml.template b/templates/ci.yml.template new file mode 100644 index 0000000..56a2480 --- /dev/null +++ b/templates/ci.yml.template @@ -0,0 +1,217 @@ +name: iOS CI + +on: + pull_request: + branches: [main, develop] + push: + branches: [main, develop] + +env: + DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer + +jobs: + ios-ci: + name: Build, Test & Quality Checks + runs-on: macos-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: "16.4" + + - name: Cache Homebrew packages + uses: actions/cache@v4 + with: + path: | + ~/Library/Caches/Homebrew + /usr/local/Homebrew + key: ${{ runner.os }}-homebrew-${{ hashFiles('**/Brewfile') }} + restore-keys: | + ${{ runner.os }}-homebrew- + + - name: Install dependencies + run: | + # Install Homebrew dependencies + brew bundle install --file=./Brewfile + + # Verify installations + echo "Installed versions:" + swiftlint --version + swiftformat --version + xcodegen --version + yq --version + + - name: Generate Xcode project + run: | + echo "Generating Xcode project..." + xcodegen generate + + PROJECT_NAME=$(yq eval '.name' project.yml) + if [ -d "${PROJECT_NAME}.xcodeproj" ]; then + echo "โœ“ Xcode project generated successfully" + else + echo "โœ— Failed to generate Xcode project" + exit 1 + fi + + - name: SwiftFormat check + run: | + echo "Checking code formatting..." + PROJECT_NAME=$(yq eval '.name' project.yml) + + if [ -d "$PROJECT_NAME" ]; then + swiftformat --lint $PROJECT_NAME + echo "โœ“ SwiftFormat check passed" + else + echo "โš  Source directory $PROJECT_NAME not found, skipping SwiftFormat" + fi + + - name: SwiftLint + run: | + echo "Running SwiftLint..." + PROJECT_NAME=$(yq eval '.name' project.yml) + + if [ -d "$PROJECT_NAME" ]; then + swiftlint lint --strict + echo "โœ“ SwiftLint passed" + else + echo "โš  Source directory $PROJECT_NAME not found, skipping SwiftLint" + fi + + - name: Build project + run: | + echo "Building project..." + PROJECT_NAME=$(yq eval '.name' project.yml) + DEPLOYMENT_TARGET=$(yq eval '.options.deploymentTarget.iOS' project.yml) + + # Determine simulator based on deployment target + if [ "$DEPLOYMENT_TARGET" != "null" ] && [ -n "$DEPLOYMENT_TARGET" ]; then + MAJOR_VERSION=$(echo $DEPLOYMENT_TARGET | cut -d'.' -f1) + if [ "$MAJOR_VERSION" -ge "17" ]; then + SIMULATOR_NAME="iPhone 16 Pro" + else + SIMULATOR_NAME="iPhone 15 Pro" + fi + else + SIMULATOR_NAME="iPhone 16 Pro" + fi + + echo "Building for $SIMULATOR_NAME..." + + xcodebuild build \ + -project "${PROJECT_NAME}.xcodeproj" \ + -scheme "$PROJECT_NAME" \ + -destination "platform=iOS Simulator,name=$SIMULATOR_NAME" \ + -configuration Debug \ + ONLY_ACTIVE_ARCH=YES \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGNING_ALLOWED=NO + + echo "โœ“ Build completed successfully" + + - name: Run unit tests + run: | + PROJECT_NAME=$(yq eval '.name' project.yml) + + # Check if test target exists + HAS_TESTS=$(yq eval ".targets | has(\"${PROJECT_NAME}Tests\")" project.yml) + + if [ "$HAS_TESTS" = "true" ]; then + echo "Running unit tests..." + + DEPLOYMENT_TARGET=$(yq eval '.options.deploymentTarget.iOS' project.yml) + if [ "$DEPLOYMENT_TARGET" != "null" ] && [ -n "$DEPLOYMENT_TARGET" ]; then + MAJOR_VERSION=$(echo $DEPLOYMENT_TARGET | cut -d'.' -f1) + if [ "$MAJOR_VERSION" -ge "17" ]; then + SIMULATOR_NAME="iPhone 16 Pro" + else + SIMULATOR_NAME="iPhone 15 Pro" + fi + else + SIMULATOR_NAME="iPhone 16 Pro" + fi + + xcodebuild test \ + -project "${PROJECT_NAME}.xcodeproj" \ + -scheme "$PROJECT_NAME" \ + -destination "platform=iOS Simulator,name=$SIMULATOR_NAME" \ + -configuration Debug \ + -enableCodeCoverage YES \ + ONLY_ACTIVE_ARCH=YES \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGNING_ALLOWED=NO + + echo "โœ“ Unit tests completed" + else + echo "โš  No unit test target found, skipping tests" + fi + + - name: Validate scripts + run: | + echo "Validating helper scripts..." + + # Check that all scripts are executable + find scripts -name "*.sh" -type f | while read script; do + if [ -x "$script" ]; then + echo "โœ“ $script is executable" + else + echo "โœ— $script is not executable" + exit 1 + fi + done + + # Check script syntax + find scripts -name "*.sh" -type f | while read script; do + if bash -n "$script"; then + echo "โœ“ $script syntax is valid" + else + echo "โœ— $script has syntax errors" + exit 1 + fi + done + + echo "โœ“ All scripts validated" + + - name: Check configuration files + run: | + echo "Validating configuration files..." + + # Check .swiftlint.yml if it exists + if [ -f ".swiftlint.yml" ]; then + yq eval '.' .swiftlint.yml > /dev/null + echo "โœ“ .swiftlint.yml is valid" + fi + + # Check .swiftformat if it exists + if [ -f ".swiftformat" ]; then + echo "โœ“ .swiftformat found" + fi + + # Check simulator.yml if it exists + if [ -f "simulator.yml" ]; then + yq eval '.' simulator.yml > /dev/null + echo "โœ“ simulator.yml is valid" + fi + + # Check Brewfile + if [ -f "Brewfile" ]; then + echo "โœ“ Brewfile found" + fi + + echo "โœ“ Configuration files validated" + + - name: CI Summary + run: | + echo "๐ŸŽ‰ CI Complete!" + echo "" + echo "โœ… Code formatting validated" + echo "โœ… Code quality checks passed" + echo "โœ… Build completed successfully" + echo "โœ… Tests executed" + echo "โœ… Configuration validated" + echo "" + echo "Ready for review! ๐Ÿš€" \ No newline at end of file