Forji/justfile
Stefan Hausotte 5adc0102eb feat: inital commit
Forji is an iOS app to interact with a Forgejo instance
2026-02-28 21:08:13 +01:00

257 lines
9.8 KiB
Makefile

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 <Class/method>"
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