diff --git a/.github/workflows/ci.yaml b/.github/workflows/test-all.yaml similarity index 77% rename from .github/workflows/ci.yaml rename to .github/workflows/test-all.yaml index ecbaa2f..190ba92 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/test-all.yaml @@ -1,11 +1,5 @@ -name: CI +name: Test All on: - push: - branches: - - main - pull_request: - branches: - - main workflow_dispatch: env: FLUTTER_VERSION: "3.29.0" @@ -21,23 +15,26 @@ jobs: - id: set-dirs name: Find project directories run: | - DIRS=$(find . -type d -maxdepth 1 -not -path "*/\.*" -not -path "./_initial" -not -path "." | jq -R -s -c 'split("\n")[:-1]') + # Find Flutter/Dart projects (with pubspec.yaml) + DIRS=$(find . -maxdepth 1 -type d -not -path "*/\.*" -not -path "./_initial" -not -path "." -exec test -f '{}/pubspec.yaml' \; -print | sed 's|^\./||' | jq -R -s -c 'split("\n")[:-1]') echo "project_dirs=${DIRS}" >> $GITHUB_OUTPUT # Count directories correctly using jq to get array length DIRS_COUNT=$(echo $DIRS | jq 'length') - # Get directories as newline-separated list + # Get directories as newline-separated list for display DIRS_LIST=$(echo $DIRS | jq -r '.[]') # Print count and list to console - echo "==== Found $DIRS_COUNT valid project directories ====" + echo "==== Found $DIRS_COUNT valid Flutter/Dart project directories ====" echo "$DIRS_LIST" | while read -r dir; do - echo "- $dir" + if [ -f "$dir/pubspec.yaml" ]; then + echo "- $dir" + fi done # Create step summary - echo "# Project Directories Found" >> $GITHUB_STEP_SUMMARY + echo "# Flutter/Dart Project Directories Found" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Total: $DIRS_COUNT**" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY @@ -78,7 +75,7 @@ jobs: make ci-test build_web: - needs: [discover_projects, unit_and_widget_tests] + needs: [discover_projects, lint, unit_and_widget_tests] name: Build Web runs-on: ubuntu-latest steps: diff --git a/.github/workflows/test-changes.yaml b/.github/workflows/test-changes.yaml new file mode 100644 index 0000000..85cb21b --- /dev/null +++ b/.github/workflows/test-changes.yaml @@ -0,0 +1,215 @@ +name: Test Changes +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: +env: + FLUTTER_VERSION: "3.29.0" +jobs: + detect_changes: + name: Detect Changed Projects + runs-on: ubuntu-latest + outputs: + all_project_dirs: ${{ steps.all-dirs.outputs.project_dirs }} + changed_project_dirs: ${{ steps.changed-dirs.outputs.changed_projects }} + has_changes: ${{ steps.changed-dirs.outputs.has_changes }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # This is important to get all history for comparison + + - id: all-dirs + name: Find all project directories + run: | + # Create a temporary file to store project directories + touch project_dirs.txt + + # Find Flutter projects and write to file - one per line + echo "Finding Flutter/Dart projects (excluding _initial and hidden directories)..." + find . -maxdepth 1 -type d -not -path "*/\.*" -not -path "./_initial" -not -path "." -exec test -f '{}/pubspec.yaml' \; -print | sed 's|^\./||' > project_dirs.txt + + # Format as a properly escaped JSON array for GitHub Actions + echo "project_dirs<> $GITHUB_OUTPUT + jq -R -s 'split("\n") | map(select(length > 0))' project_dirs.txt >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # Count projects found + DIRS_COUNT=$(cat project_dirs.txt | grep -v '^$' | wc -l) + + # Print count and list to console + echo "==== Found $DIRS_COUNT valid project directories ====" + cat project_dirs.txt | while read -r dir; do + if [ ! -z "$dir" ]; then + echo "- $dir" + fi + done + + # Debug: List all top-level directories for reference + echo "==== All top-level directories ====" + find . -maxdepth 1 -type d -not -path "." | sort + + - id: changed-dirs + name: Detect changed projects + run: | + # Get base commit to compare against + if [ "${{ github.event_name }}" == "pull_request" ]; then + BASE_COMMIT=${{ github.event.pull_request.base.sha }} + else + # For push events, compare with the parent commit + BASE_COMMIT=$(git rev-parse HEAD^) + fi + + # Find all changed files + CHANGED_FILES=$(git diff --name-only $BASE_COMMIT HEAD) + echo "Changed files:" + echo "$CHANGED_FILES" + + # Filter out files in ignored directories like _initial + FILTERED_CHANGED_FILES=$(echo "$CHANGED_FILES" | grep -v "^_initial/" | grep -v "^\.github/") + echo "Filtered changed files (excluding _initial and .github):" + echo "$FILTERED_CHANGED_FILES" + + # Read all project directories + ALL_PROJECTS=$(cat project_dirs.txt) + + # Print the projects for debugging + echo "All projects (from file):" + cat project_dirs.txt + + # Find which projects contain changed files + CHANGED_PROJECTS=() + echo "==== DEBUG: Checking projects for changes ====" + + # Use while loop with read to properly handle line breaks + while read -r PROJECT || [ -n "$PROJECT" ]; do + if [ -z "$PROJECT" ]; then continue; fi + + # Remove ./ prefix if present and trim whitespace + CLEAN_PROJECT=$(echo "$PROJECT" | sed 's|^\./||' | xargs) + echo "Checking project: '$CLEAN_PROJECT'" + + # Check if any changed files match this project path + if echo "$FILTERED_CHANGED_FILES" | grep -q "^$CLEAN_PROJECT/"; then + echo " ✅ Found changes for $CLEAN_PROJECT" + CHANGED_PROJECTS+=("$CLEAN_PROJECT") + else + echo " ❌ No changes detected for $CLEAN_PROJECT" + # Check individual files for debugging + echo " Looking for patterns like: ^$CLEAN_PROJECT/" + echo "$FILTERED_CHANGED_FILES" | while read -r FILE; do + if [[ "$FILE" == $CLEAN_PROJECT/* ]]; then + echo " Should match: $FILE" + fi + done + fi + done < project_dirs.txt + + # Check if any projects have changed + if [ ${#CHANGED_PROJECTS[@]} -eq 0 ]; then + echo "has_changes=false" >> $GITHUB_OUTPUT + echo "No projects have changes" + # Use empty array but valid JSON + echo "changed_projects=[]" >> $GITHUB_OUTPUT + + # Add debug info + echo "==== DEBUG: No changed projects detected ====" + echo "All detected projects:" + cat project_dirs.txt + echo "Filtered changed files:" + echo "$FILTERED_CHANGED_FILES" + else + echo "has_changes=true" >> $GITHUB_OUTPUT + + # Format as properly escaped JSON array using EOF delimiter + echo "changed_projects<> $GITHUB_OUTPUT + printf '%s\n' "${CHANGED_PROJECTS[@]}" | jq -R -s 'split("\n") | map(select(length > 0))' >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # Print changed projects + echo "==== Found ${#CHANGED_PROJECTS[@]} changed projects ====" + for PROJECT in "${CHANGED_PROJECTS[@]}"; do + echo "- $PROJECT" + # Show which files changed in this project + echo " Changed files:" + echo "$FILTERED_CHANGED_FILES" | grep "^$PROJECT/" | sed 's/^/ /' + done + + # Extra debug info to confirm the output is correctly formatted + echo "==== DEBUG: GitHub Actions Output ====" + echo "has_changes=true" + echo "changed_projects:" + printf '%s\n' "${CHANGED_PROJECTS[@]}" | jq -R -s 'split("\n") | map(select(length > 0))' + fi + + # Create step summary + echo "# Changed Projects" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Total Changed: ${#CHANGED_PROJECTS[@]}**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + for PROJECT in "${CHANGED_PROJECTS[@]}"; do + echo "- \`$PROJECT\`" >> $GITHUB_STEP_SUMMARY + done + + lint: + name: Lint and Format Check + needs: detect_changes + runs-on: ubuntu-latest + if: needs.detect_changes.outputs.has_changes == 'true' + strategy: + matrix: + project: ${{ fromJson(needs.detect_changes.outputs.changed_project_dirs) }} + fail-fast: false + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} + + - name: Run lint and format check + run: | + make ci-lint-project PROJECT=${{ matrix.project }} + + unit_and_widget_tests: + needs: [detect_changes, lint] + name: Unit and Widget tests + runs-on: ubuntu-latest + if: needs.detect_changes.outputs.has_changes == 'true' + strategy: + matrix: + project: ${{ fromJson(needs.detect_changes.outputs.changed_project_dirs) }} + fail-fast: false + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} + + - name: Run tests + run: | + make ci-test-project PROJECT=${{ matrix.project }} + + build_web: + needs: [detect_changes, lint, unit_and_widget_tests] + name: Build Web + runs-on: ubuntu-latest + if: needs.detect_changes.outputs.has_changes == 'true' + strategy: + matrix: + project: ${{ fromJson(needs.detect_changes.outputs.changed_project_dirs) }} + fail-fast: false + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} + + - name: Build web + run: | + make ci-build-project PROJECT=${{ matrix.project }} diff --git a/Makefile b/Makefile index a782d31..c323964 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,95 @@ build-web-all: cd $$d && flutter build web && cd -; \ done +# Individual project commands +.PHONY: get-project +get-project: + @if [ -z "$(PROJECT)" ]; then \ + echo "Error: PROJECT parameter is required. Usage: make get-project PROJECT="; \ + exit 1; \ + fi + @if [ ! -d "$(PROJECT)" ] || [ ! -f "$(PROJECT)/pubspec.yaml" ]; then \ + echo "Error: $(PROJECT) is not a valid Flutter/Dart project directory"; \ + exit 1; \ + fi + @echo "=== Getting dependencies for $(PROJECT) ==="; + @cd $(PROJECT) && flutter pub get + +.PHONY: format-project +format-project: + @if [ -z "$(PROJECT)" ]; then \ + echo "Error: PROJECT parameter is required. Usage: make format-project PROJECT="; \ + exit 1; \ + fi + @if [ ! -d "$(PROJECT)" ] || [ ! -f "$(PROJECT)/pubspec.yaml" ]; then \ + echo "Error: $(PROJECT) is not a valid Flutter/Dart project directory"; \ + exit 1; \ + fi + @echo "=== Formatting $(PROJECT) ==="; + @cd $(PROJECT) && dart format lib test + +.PHONY: format-check-project +format-check-project: + @if [ -z "$(PROJECT)" ]; then \ + echo "Error: PROJECT parameter is required. Usage: make format-check-project PROJECT="; \ + exit 1; \ + fi + @if [ ! -d "$(PROJECT)" ] || [ ! -f "$(PROJECT)/pubspec.yaml" ]; then \ + echo "Error: $(PROJECT) is not a valid Flutter/Dart project directory"; \ + exit 1; \ + fi + @echo "=== Checking format for $(PROJECT) ==="; + @cd $(PROJECT) && dart format $$(find lib -name "*.dart" -not \( -name "*.*freezed.dart" -o -name "*.*g.dart" \) ) --set-exit-if-changed + +.PHONY: analyze-project +analyze-project: + @if [ -z "$(PROJECT)" ]; then \ + echo "Error: PROJECT parameter is required. Usage: make analyze-project PROJECT="; \ + exit 1; \ + fi + @if [ ! -d "$(PROJECT)" ] || [ ! -f "$(PROJECT)/pubspec.yaml" ]; then \ + echo "Error: $(PROJECT) is not a valid Flutter/Dart project directory"; \ + exit 1; \ + fi + @echo "=== Analyzing $(PROJECT) ==="; + @cd $(PROJECT) && dart analyze . --no-fatal-warnings + +.PHONY: test-project +test-project: + @if [ -z "$(PROJECT)" ]; then \ + echo "Error: PROJECT parameter is required. Usage: make test-project PROJECT="; \ + exit 1; \ + fi + @if [ ! -d "$(PROJECT)" ] || [ ! -f "$(PROJECT)/pubspec.yaml" ]; then \ + echo "Error: $(PROJECT) is not a valid Flutter/Dart project directory"; \ + exit 1; \ + fi + @echo "=== Testing $(PROJECT) ==="; + @cd $(PROJECT) && flutter test --exclude-tags=golden + +.PHONY: build-web-project +build-web-project: + @if [ -z "$(PROJECT)" ]; then \ + echo "Error: PROJECT parameter is required. Usage: make build-web-project PROJECT="; \ + exit 1; \ + fi + @if [ ! -d "$(PROJECT)" ] || [ ! -f "$(PROJECT)/pubspec.yaml" ]; then \ + echo "Error: $(PROJECT) is not a valid Flutter/Dart project directory"; \ + exit 1; \ + fi + @echo "=== Building web for $(PROJECT) ==="; + @cd $(PROJECT) && flutter build web + +# CI commands for individual projects +.PHONY: ci-lint-project +ci-lint-project: get-project analyze-project format-check-project + +.PHONY: ci-test-project +ci-test-project: get-project test-project + +.PHONY: ci-build-project +ci-build-project: get-project build-web-project + # CI commands .PHONY: ci-lint ci-lint: get-all analyze-all format-check @@ -72,4 +161,15 @@ help: @echo " make ci-lint - Run all lint-related checks (used in CI)" @echo " make ci-test - Run all tests (used in CI)" @echo " make check-all - Run all checks locally" - @echo " make help - Show this help message" \ No newline at end of file + @echo " make help - Show this help message" + @echo "" + @echo "Individual project commands:" + @echo " make get-project PROJECT= - Get dependencies for a specific project" + @echo " make test-project PROJECT= - Run tests for a specific project" + @echo " make format-project PROJECT= - Format code in a specific project" + @echo " make format-check-project PROJECT= - Check if code is properly formatted in a specific project" + @echo " make analyze-project PROJECT= - Analyze code in a specific project" + @echo " make build-web-project PROJECT= - Build web for a specific project" + @echo " make ci-lint-project PROJECT= - Run lint-related checks for a specific project" + @echo " make ci-test-project PROJECT= - Run tests for a specific project" + @echo " make ci-build-project PROJECT= - Build web for a specific project" \ No newline at end of file diff --git a/_initial/finish-workout/lib/workout/workout_repository.dart b/_initial/finish-workout/lib/workout/workout_repository.dart index a6d37d0..f628817 100644 --- a/_initial/finish-workout/lib/workout/workout_repository.dart +++ b/_initial/finish-workout/lib/workout/workout_repository.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:demo/auth/user.dart'; import 'package:demo/core/abstractions/database_abstraction.dart'; class WorkoutRepository { diff --git a/_initial/finish-workout/test/workout_test.dart b/_initial/finish-workout/test/workout_test.dart index f47493f..38b04cc 100644 --- a/_initial/finish-workout/test/workout_test.dart +++ b/_initial/finish-workout/test/workout_test.dart @@ -1,6 +1,5 @@ import 'package:demo/auth/user.dart'; import 'package:demo/auth/user_service.dart'; -import 'package:demo/core/abstractions/database_abstraction.dart'; import 'package:demo/core/locator.dart'; import 'package:demo/workout/workout_repository.dart'; import 'package:demo/workout/workout_view.dart'; diff --git a/add-remove-ui/lib/workout/workout_repository.dart b/add-remove-ui/lib/workout/workout_repository.dart index a6d37d0..f628817 100644 --- a/add-remove-ui/lib/workout/workout_repository.dart +++ b/add-remove-ui/lib/workout/workout_repository.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:demo/auth/user.dart'; import 'package:demo/core/abstractions/database_abstraction.dart'; class WorkoutRepository { diff --git a/add-remove-ui/test/workout_test.dart b/add-remove-ui/test/workout_test.dart index 1b9474d..c11fe48 100644 --- a/add-remove-ui/test/workout_test.dart +++ b/add-remove-ui/test/workout_test.dart @@ -1,6 +1,5 @@ import 'package:demo/auth/user.dart'; import 'package:demo/auth/user_service.dart'; -import 'package:demo/core/abstractions/database_abstraction.dart'; import 'package:demo/core/locator.dart'; import 'package:demo/workout/workout_repository.dart'; import 'package:demo/workout/workout_view.dart'; diff --git a/exercise-styling/lib/workout/workout_repository.dart b/exercise-styling/lib/workout/workout_repository.dart index a6d37d0..f628817 100644 --- a/exercise-styling/lib/workout/workout_repository.dart +++ b/exercise-styling/lib/workout/workout_repository.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:demo/auth/user.dart'; import 'package:demo/core/abstractions/database_abstraction.dart'; class WorkoutRepository { diff --git a/exercise-styling/test/workout_test.dart b/exercise-styling/test/workout_test.dart index 29a72e5..fd8d9d3 100644 --- a/exercise-styling/test/workout_test.dart +++ b/exercise-styling/test/workout_test.dart @@ -1,6 +1,5 @@ import 'package:demo/auth/user.dart'; import 'package:demo/auth/user_service.dart'; -import 'package:demo/core/abstractions/database_abstraction.dart'; import 'package:demo/core/locator.dart'; import 'package:demo/workout/workout_repository.dart'; import 'package:demo/workout/workout_view.dart'; diff --git a/finish-workout-button/lib/workout/workout_repository.dart b/finish-workout-button/lib/workout/workout_repository.dart index a6d37d0..f628817 100644 --- a/finish-workout-button/lib/workout/workout_repository.dart +++ b/finish-workout-button/lib/workout/workout_repository.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:demo/auth/user.dart'; import 'package:demo/core/abstractions/database_abstraction.dart'; class WorkoutRepository { diff --git a/finish-workout-button/test/workout_test.dart b/finish-workout-button/test/workout_test.dart index 40faa5f..9fba34c 100644 --- a/finish-workout-button/test/workout_test.dart +++ b/finish-workout-button/test/workout_test.dart @@ -1,6 +1,5 @@ import 'package:demo/auth/user.dart'; import 'package:demo/auth/user_service.dart'; -import 'package:demo/core/abstractions/database_abstraction.dart'; import 'package:demo/core/locator.dart'; import 'package:demo/workout/workout_repository.dart'; import 'package:demo/workout/workout_view.dart'; diff --git a/finish-workout/lib/workout/workout_repository.dart b/finish-workout/lib/workout/workout_repository.dart index a6d37d0..f628817 100644 --- a/finish-workout/lib/workout/workout_repository.dart +++ b/finish-workout/lib/workout/workout_repository.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:demo/auth/user.dart'; import 'package:demo/core/abstractions/database_abstraction.dart'; class WorkoutRepository { diff --git a/finish-workout/test/workout_test.dart b/finish-workout/test/workout_test.dart index f47493f..38b04cc 100644 --- a/finish-workout/test/workout_test.dart +++ b/finish-workout/test/workout_test.dart @@ -1,6 +1,5 @@ import 'package:demo/auth/user.dart'; import 'package:demo/auth/user_service.dart'; -import 'package:demo/core/abstractions/database_abstraction.dart'; import 'package:demo/core/locator.dart'; import 'package:demo/workout/workout_repository.dart'; import 'package:demo/workout/workout_view.dart'; diff --git a/load-previous/lib/workout/workout_repository.dart b/load-previous/lib/workout/workout_repository.dart index 085f6a3..5053552 100644 --- a/load-previous/lib/workout/workout_repository.dart +++ b/load-previous/lib/workout/workout_repository.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:demo/auth/user.dart'; import 'package:demo/core/abstractions/database_abstraction.dart'; class WorkoutRepository { diff --git a/load-previous/test/workout_test.dart b/load-previous/test/workout_test.dart index 22ca6e0..5005685 100644 --- a/load-previous/test/workout_test.dart +++ b/load-previous/test/workout_test.dart @@ -1,6 +1,5 @@ import 'package:demo/auth/user.dart'; import 'package:demo/auth/user_service.dart'; -import 'package:demo/core/abstractions/database_abstraction.dart'; import 'package:demo/core/locator.dart'; import 'package:demo/workout/workout_repository.dart'; import 'package:demo/workout/workout_view.dart'; diff --git a/show-previous-final/lib/workout/workout_repository.dart b/show-previous-final/lib/workout/workout_repository.dart index 085f6a3..5053552 100644 --- a/show-previous-final/lib/workout/workout_repository.dart +++ b/show-previous-final/lib/workout/workout_repository.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:demo/auth/user.dart'; import 'package:demo/core/abstractions/database_abstraction.dart'; class WorkoutRepository { diff --git a/show-previous-final/test/workout_test.dart b/show-previous-final/test/workout_test.dart index 7f633ad..7c35b26 100644 --- a/show-previous-final/test/workout_test.dart +++ b/show-previous-final/test/workout_test.dart @@ -1,6 +1,5 @@ import 'package:demo/auth/user.dart'; import 'package:demo/auth/user_service.dart'; -import 'package:demo/core/abstractions/database_abstraction.dart'; import 'package:demo/core/locator.dart'; import 'package:demo/workout/workout_repository.dart'; import 'package:demo/workout/workout_view.dart'; diff --git a/show-previous/lib/workout/workout_repository.dart b/show-previous/lib/workout/workout_repository.dart index 085f6a3..5053552 100644 --- a/show-previous/lib/workout/workout_repository.dart +++ b/show-previous/lib/workout/workout_repository.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:demo/auth/user.dart'; import 'package:demo/core/abstractions/database_abstraction.dart'; class WorkoutRepository { diff --git a/show-previous/test/workout_test.dart b/show-previous/test/workout_test.dart index db7f089..ccadcc6 100644 --- a/show-previous/test/workout_test.dart +++ b/show-previous/test/workout_test.dart @@ -1,6 +1,5 @@ import 'package:demo/auth/user.dart'; import 'package:demo/auth/user_service.dart'; -import 'package:demo/core/abstractions/database_abstraction.dart'; import 'package:demo/core/locator.dart'; import 'package:demo/workout/workout_repository.dart'; import 'package:demo/workout/workout_view.dart'; diff --git a/workout-list/lib/workout/workout_repository.dart b/workout-list/lib/workout/workout_repository.dart index a6d37d0..f628817 100644 --- a/workout-list/lib/workout/workout_repository.dart +++ b/workout-list/lib/workout/workout_repository.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:demo/auth/user.dart'; import 'package:demo/core/abstractions/database_abstraction.dart'; class WorkoutRepository { diff --git a/workout-list/test/workout_test.dart b/workout-list/test/workout_test.dart index 93840e7..49058ad 100644 --- a/workout-list/test/workout_test.dart +++ b/workout-list/test/workout_test.dart @@ -1,6 +1,5 @@ import 'package:demo/auth/user.dart'; import 'package:demo/auth/user_service.dart'; -import 'package:demo/core/abstractions/database_abstraction.dart'; import 'package:demo/core/locator.dart'; import 'package:demo/workout/workout_repository.dart'; import 'package:demo/workout/workout_view.dart';