set shell := ["bash", "-eo", "pipefail", "-c"] default_destination := "platform=iOS Simulator,name=iPhone 17 Pro" default_destination_b := "platform=iOS Simulator,name=iPhone Air" compose_file := "integration/docker-compose.yml" base_url_1 := "http://localhost:13001" base_url_2 := "http://localhost:13002" readonly_classes := "LoginUITests CommitHistoryUITests PaginationUITests RepositoryUITests IssueUITests PullRequestUITests OverviewCreateUITests" mutating_classes := "RepositoryMutatingUITests IssueMutatingUITests PullRequestMutatingUITests OverviewCreateMutatingUITests HomeScreenUITests NotificationsUITests PermissionUITests" # List all available tasks default: @just --list # Build the app build destination=default_destination: xcodebuild -project Forji/Forji.xcodeproj -scheme Forji -destination '{{destination}}' build 2>&1 | xcbeautify # Run app unit tests test destination=default_destination: xcodebuild -project Forji/Forji.xcodeproj -scheme Forji -destination '{{destination}}' test -only-testing:ForjiTests 2>&1 | xcbeautify # Lint Swift code lint: swiftlint lint Forji/Forji # Format Swift code format: swiftformat Forji/Forji # Clean build artifacts clean: xcodebuild -project Forji/Forji.xcodeproj -scheme Forji clean 2>&1 | xcbeautify # Build, install, and launch in simulator run destination=default_destination: #!/usr/bin/env bash set -eo pipefail SIM_NAME="$(echo '{{destination}}' | sed -n 's/.*name=\([^,]*\).*/\1/p')" xcrun simctl boot "$SIM_NAME" 2>/dev/null || true open -a Simulator xcodebuild -project Forji/Forji.xcodeproj -scheme Forji -destination '{{destination}}' build 2>&1 | xcbeautify BUILT_APP="$(xcodebuild -project Forji/Forji.xcodeproj -scheme Forji -destination '{{destination}}' -showBuildSettings 2>/dev/null | grep ' BUILT_PRODUCTS_DIR' | awk '{print $3}')/Forji.app" xcrun simctl install "$SIM_NAME" "$BUILT_APP" xcrun simctl launch "$SIM_NAME" "$(defaults read "$BUILT_APP/Info.plist" CFBundleIdentifier)" # List all UI integration tests test-list: @grep -rh 'func test.*()' Forji/ForjiUITests/*.swift \ | grep -v 'override\|private\|ForgejoUITestBase' \ | sed 's/.*func //' | sed 's/().*//' \ | while read -r method; do \ file=$(grep -rl "func $method()" Forji/ForjiUITests/*.swift | head -1); \ class=$(grep 'class.*:' "$file" | head -1 | sed 's/.*class //' | sed 's/[: ].*//' ); \ printf " %s/%s\n" "$class" "$method"; \ done | sort # Start Forgejo Docker containers docker-up: docker compose -f {{compose_file}} up -d --wait --wait-timeout 120 # Stop Forgejo Docker containers and clean up docker-down: docker compose -f {{compose_file}} down -v 2>/dev/null || true rm -f /tmp/forgejo_test_url.txt /tmp/forgejo_test_url_*.txt # Seed test data into Forgejo instances (with snapshot caching) seed: #!/usr/bin/env bash set -eo pipefail COMPOSE_FILE="{{compose_file}}" BASE_URL_1="{{base_url_1}}" BASE_URL_2="{{base_url_2}}" SNAPSHOT_FILE="integration/.forgejo-seed-snapshot.tar.gz" HASH_FILE="integration/.forgejo-seed-hash" SETUP_HASH=$(shasum -a 256 integration/setup.sh | awk '{print $1}') wait_for_instance() { local url="$1" elapsed=0 until curl -sf "$url/api/v1/version" > /dev/null 2>&1; do if [ "$elapsed" -ge 60 ]; then echo "Timed out waiting for $url after 60s" exit 1 fi sleep 1 elapsed=$((elapsed + 1)) done echo " $url is ready (${elapsed}s)" } if [ -f "$SNAPSHOT_FILE" ] && [ -f "$HASH_FILE" ] && [ "$(cat "$HASH_FILE")" = "$SETUP_HASH" ]; then echo "Restoring from seed snapshot..." CONTAINER_1=$(docker compose -f "$COMPOSE_FILE" ps -q forgejo-1) CONTAINER_2=$(docker compose -f "$COMPOSE_FILE" ps -q forgejo-2) docker cp "$SNAPSHOT_FILE" "$CONTAINER_1:/tmp/snapshot.tar.gz" & docker cp "$SNAPSHOT_FILE" "$CONTAINER_2:/tmp/snapshot.tar.gz" & wait docker exec "$CONTAINER_1" sh -c "cd / && tar xzf /tmp/snapshot.tar.gz" & docker exec "$CONTAINER_2" sh -c "cd / && tar xzf /tmp/snapshot.tar.gz" & wait docker compose -f "$COMPOSE_FILE" restart forgejo-1 forgejo-2 wait_for_instance "$BASE_URL_1" & wait_for_instance "$BASE_URL_2" & wait echo "Snapshot restored to both instances." else echo "Seeding test data..." bash integration/setup.sh "$BASE_URL_1" forgejo-1 echo "Snapshotting seed data..." CONTAINER_1=$(docker compose -f "$COMPOSE_FILE" ps -q forgejo-1) docker exec "$CONTAINER_1" sh -c "cd / && tar czf /tmp/snapshot.tar.gz data" docker cp "$CONTAINER_1:/tmp/snapshot.tar.gz" "$SNAPSHOT_FILE" echo "$SETUP_HASH" > "$HASH_FILE" echo "Restoring snapshot to instance 2..." CONTAINER_2=$(docker compose -f "$COMPOSE_FILE" ps -q forgejo-2) docker cp "$SNAPSHOT_FILE" "$CONTAINER_2:/tmp/snapshot.tar.gz" docker exec "$CONTAINER_2" sh -c "cd / && tar xzf /tmp/snapshot.tar.gz" docker compose -f "$COMPOSE_FILE" restart forgejo-2 wait_for_instance "$BASE_URL_2" echo "Instance 2 seeded from snapshot." fi # Run read-only UI tests (assumes build + seed + URL files are set up) test-readonly destination=default_destination: #!/usr/bin/env bash set -eo pipefail ARGS="" for cls in {{readonly_classes}}; do ARGS="$ARGS -only-testing:ForjiUITests/$cls" done xcodebuild test-without-building \ -project Forji/Forji.xcodeproj \ -scheme Forji \ -destination '{{destination}}' \ -parallel-testing-enabled NO \ $ARGS 2>&1 | xcbeautify # Run mutating UI tests (assumes build + seed + URL files are set up) test-mutating destination=default_destination_b: #!/usr/bin/env bash set -eo pipefail ARGS="" for cls in {{mutating_classes}}; do ARGS="$ARGS -only-testing:ForjiUITests/$cls" done xcodebuild test-without-building \ -project Forji/Forji.xcodeproj \ -scheme Forji \ -destination '{{destination}}' \ -parallel-testing-enabled NO \ $ARGS 2>&1 | xcbeautify # Run full UI integration test suite (requires Docker) test-ui destination_a=default_destination destination_b=default_destination_b: #!/usr/bin/env bash set -eo pipefail cleanup() { just docker-down } trap cleanup EXIT just sim-boot '{{destination_a}}' '{{destination_b}}' & just docker-up & just build-for-testing '{{destination_a}}' '{{destination_b}}' & wait just seed just _write-url-files '{{destination_a}}' '{{destination_b}}' LOG_A="/tmp/forji_test_group_a.log" LOG_B="/tmp/forji_test_group_b.log" EXIT_A=0 EXIT_B=0 echo "Starting read-only tests..." just test-readonly '{{destination_a}}' > "$LOG_A" 2>&1 & PID_A=$! echo "Starting mutating tests..." just test-mutating '{{destination_b}}' > "$LOG_B" 2>&1 & PID_B=$! wait $PID_A || EXIT_A=$? wait $PID_B || EXIT_B=$? echo "=== Read-only tests output ===" cat "$LOG_A" echo "" echo "=== Mutating tests output ===" cat "$LOG_B" rm -f "$LOG_A" "$LOG_B" if [ "$EXIT_A" -ne 0 ]; then echo "Read-only tests failed (exit code $EXIT_A)." fi if [ "$EXIT_B" -ne 0 ]; then echo "Mutating tests failed (exit code $EXIT_B)." fi if [ "$EXIT_A" -ne 0 ] || [ "$EXIT_B" -ne 0 ]; then echo "Integration tests FAILED." exit 1 fi echo "" echo "All integration tests passed." # Run a single UI test (requires Docker). Use `just test-list` to see available tests. test-one filter="" destination=default_destination: #!/usr/bin/env bash set -eo pipefail FILTER="{{filter}}" if [ -z "$FILTER" ]; then echo "Usage: just test-one " echo "" echo "Run 'just test-list' to see available tests." exit 1 fi cleanup() { just docker-down } trap cleanup EXIT SIM_NAME="$(echo '{{destination}}' | sed -n 's/.*name=\([^,]*\).*/\1/p')" xcrun simctl boot "$SIM_NAME" 2>/dev/null || true just docker-up & xcodebuild build-for-testing \ -project Forji/Forji.xcodeproj \ -scheme Forji \ -destination '{{destination}}' 2>&1 | xcbeautify & wait just seed SIM_SAFE="${SIM_NAME// /_}" echo "{{base_url_1}}" > "/tmp/forgejo_test_url_${SIM_SAFE}.txt" echo "{{base_url_1}}" > /tmp/forgejo_test_url.txt xcodebuild test-without-building \ -project Forji/Forji.xcodeproj \ -scheme Forji \ -destination '{{destination}}' \ -parallel-testing-enabled NO \ -only-testing:"ForjiUITests/$FILTER" 2>&1 | xcbeautify [private] sim-boot destination_a=default_destination destination_b=default_destination_b: #!/usr/bin/env bash set -eo pipefail SIM_A="$(echo '{{destination_a}}' | sed -n 's/.*name=\([^,]*\).*/\1/p')" SIM_B="$(echo '{{destination_b}}' | sed -n 's/.*name=\([^,]*\).*/\1/p')" xcrun simctl boot "$SIM_A" 2>/dev/null || true xcrun simctl boot "$SIM_B" 2>/dev/null || true [private] build-for-testing destination_a=default_destination destination_b=default_destination_b: xcodebuild build-for-testing \ -project Forji/Forji.xcodeproj \ -scheme Forji \ -destination '{{destination_a}}' \ -destination '{{destination_b}}' 2>&1 | xcbeautify [private] _write-url-files destination_a=default_destination destination_b=default_destination_b: #!/usr/bin/env bash set -eo pipefail SIM_A="$(echo '{{destination_a}}' | sed -n 's/.*name=\([^,]*\).*/\1/p')" SIM_B="$(echo '{{destination_b}}' | sed -n 's/.*name=\([^,]*\).*/\1/p')" SIM_A_SAFE="${SIM_A// /_}" SIM_B_SAFE="${SIM_B// /_}" echo "{{base_url_1}}" > "/tmp/forgejo_test_url_${SIM_A_SAFE}.txt" echo "{{base_url_2}}" > "/tmp/forgejo_test_url_${SIM_B_SAFE}.txt" echo "{{base_url_1}}" > /tmp/forgejo_test_url.txt