Add solution for Challenge 5 (#838) #679
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Update Scoreboards | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - 'challenge-*/submissions/**' | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: update-scoreboards-classic | |
| cancel-in-progress: false | |
| jobs: | |
| update-scoreboards: | |
| runs-on: ubuntu-latest | |
| if: github.repository == 'RezaSi/go-interview-practice' | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v3 | |
| with: | |
| fetch-depth: 2 # Need to compare with previous commit | |
| - name: Set up Go | |
| uses: actions/setup-go@v4 | |
| with: | |
| go-version: '1.25.0' | |
| - name: Detect changed challenges | |
| id: detect-changes | |
| run: | | |
| # Get list of changed files in this push | |
| CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD) | |
| echo "Changed files:" | |
| echo "$CHANGED_FILES" | |
| # Extract unique challenge directories that have submission changes | |
| CHANGED_CHALLENGES=$(echo "$CHANGED_FILES" | grep -E "challenge-[0-9]+/submissions/" | cut -d'/' -f1 | sort -u || true) | |
| if [ -z "$CHANGED_CHALLENGES" ]; then | |
| echo "No challenge submissions were modified" | |
| echo "has_changes=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| echo "Challenges with submission changes:" | |
| echo "$CHANGED_CHALLENGES" | |
| # Save challenges to file to avoid subshell issues | |
| echo "$CHANGED_CHALLENGES" > /tmp/changed_challenges.txt | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| - name: Update scoreboards for changed challenges | |
| if: steps.detect-changes.outputs.has_changes == 'true' | |
| run: | | |
| echo "📊 Processing changed challenges..." | |
| # Read challenges from file (avoids subshell issues) | |
| while IFS= read -r challenge_dir; do | |
| [ -n "$challenge_dir" ] || continue | |
| echo "📊 Processing $challenge_dir" | |
| # Ensure go.mod exists and handle dependencies | |
| if [ ! -f "$challenge_dir/go.mod" ]; then | |
| echo "Creating go.mod for $challenge_dir" | |
| (cd "$challenge_dir" && go mod init "$challenge_dir") | |
| fi | |
| # Run go mod tidy to ensure dependencies are correct | |
| (cd "$challenge_dir" && go mod tidy 2>/dev/null || true) | |
| # Initialize scoreboard | |
| scoreboard="$challenge_dir/SCOREBOARD.md" | |
| echo "# Scoreboard for $challenge_dir" > "$scoreboard" | |
| echo "| Username | Passed Tests | Total Tests |" >> "$scoreboard" | |
| echo "|------------|--------------|-------------|" >> "$scoreboard" | |
| # Check if submissions directory exists | |
| if [ ! -d "$challenge_dir/submissions" ]; then | |
| echo "⚠️ No submissions directory found for $challenge_dir" | |
| continue | |
| fi | |
| # Run tests for all submissions in this challenge | |
| for submission_dir in "$challenge_dir"/submissions/*/; do | |
| [ -d "$submission_dir" ] || continue | |
| USERNAME=$(basename "$submission_dir") | |
| echo "🧪 Testing submission from $USERNAME" | |
| # Backup existing solution files to avoid conflicts | |
| temp_dir=$(mktemp -d) | |
| cp "$challenge_dir"/*.go "$temp_dir/" 2>/dev/null || true | |
| # Copy participant's *.go files (excluding test files) | |
| find "$submission_dir" -name "*.go" ! -name "*_test.go" -exec cp {} "$challenge_dir/" \; 2>/dev/null || true | |
| # Run tests and capture output | |
| (cd "$challenge_dir" && timeout 60 go test -v) > "$submission_dir/test_results.txt" 2>&1 || true | |
| # Parse test results - ensure clean integer values | |
| PASS_COUNT=$(grep -c "^[[:space:]]*--- PASS: " "$submission_dir/test_results.txt" 2>/dev/null || echo "0") | |
| FAIL_COUNT=$(grep -c "^[[:space:]]*--- FAIL: " "$submission_dir/test_results.txt" 2>/dev/null || echo "0") | |
| # Clean variables and ensure they're integers | |
| PASS_COUNT=$(echo "$PASS_COUNT" | tr -d '[:space:]' | grep -o '[0-9]*' | head -1) | |
| FAIL_COUNT=$(echo "$FAIL_COUNT" | tr -d '[:space:]' | grep -o '[0-9]*' | head -1) | |
| # Default to 0 if empty | |
| PASS_COUNT=${PASS_COUNT:-0} | |
| FAIL_COUNT=${FAIL_COUNT:-0} | |
| TOTAL_TESTS=$((PASS_COUNT + FAIL_COUNT)) | |
| # If no tests found, check for other indicators | |
| if [ "$TOTAL_TESTS" -eq 0 ]; then | |
| if grep -q "PASS" "$submission_dir/test_results.txt" 2>/dev/null; then | |
| TOTAL_TESTS=1 | |
| PASS_COUNT=1 | |
| elif grep -q "FAIL\|panic\|error" "$submission_dir/test_results.txt" 2>/dev/null; then | |
| TOTAL_TESTS=1 | |
| PASS_COUNT=0 | |
| fi | |
| fi | |
| echo " Results: $PASS_COUNT/$TOTAL_TESTS tests passed" | |
| # Update scoreboard | |
| echo "| $USERNAME | $PASS_COUNT | $TOTAL_TESTS |" >> "$scoreboard" | |
| # Restore original files | |
| rm -f "$challenge_dir"/*.go | |
| cp "$temp_dir"/*.go "$challenge_dir/" 2>/dev/null || true | |
| rm -rf "$temp_dir" | |
| done | |
| # Sort scoreboard by passed tests (descending) | |
| if [ -s "$scoreboard" ] && [ $(wc -l < "$scoreboard") -gt 3 ]; then | |
| temp_sorted=$(mktemp) | |
| head -n 3 "$scoreboard" > "$temp_sorted" | |
| tail -n +4 "$scoreboard" | sort -t '|' -k3,3nr >> "$temp_sorted" | |
| mv "$temp_sorted" "$scoreboard" | |
| fi | |
| echo "✅ Completed $challenge_dir" | |
| done < /tmp/changed_challenges.txt | |
| - name: Commit scoreboard changes | |
| if: steps.detect-changes.outputs.has_changes == 'true' | |
| run: | | |
| git config user.name "GitHub Actions" | |
| git config user.email "actions@github.com" | |
| # Get list of changed challenges | |
| CHALLENGE_LIST=$(cat /tmp/changed_challenges.txt | tr '\n' ' ') | |
| # Add only the scoreboards for changed challenges | |
| while IFS= read -r challenge_dir; do | |
| [ -n "$challenge_dir" ] || continue | |
| git add "$challenge_dir/SCOREBOARD.md" | |
| done < /tmp/changed_challenges.txt | |
| if git diff --staged --quiet; then | |
| echo "No changes to commit" | |
| else | |
| git commit -m "📊 Update scoreboards for: $CHALLENGE_LIST | |
| - Updated test results for modified submissions | |
| - Refreshed completion statistics" | |
| # Robust push with retry logic for concurrent workflows | |
| MAX_RETRIES=5 | |
| RETRY_COUNT=0 | |
| while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do | |
| echo "Push attempt $((RETRY_COUNT + 1))/$MAX_RETRIES" | |
| # Pull latest changes before pushing | |
| git fetch origin main | |
| # Check if we need to rebase | |
| if ! git diff --quiet HEAD origin/main; then | |
| echo "Remote has new changes, rebasing..." | |
| git rebase origin/main || { | |
| echo "Rebase failed, trying merge strategy..." | |
| git rebase --abort 2>/dev/null || true | |
| git merge origin/main -m "Merge remote changes before scoreboard update" | |
| } | |
| fi | |
| # Try to push | |
| if git push origin main; then | |
| echo "✅ Successfully pushed changes" | |
| break | |
| else | |
| echo "❌ Push failed, retrying in $((RETRY_COUNT + 1)) seconds..." | |
| sleep $((RETRY_COUNT + 1)) | |
| RETRY_COUNT=$((RETRY_COUNT + 1)) | |
| fi | |
| done | |
| if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then | |
| echo "🚨 Failed to push after $MAX_RETRIES attempts" | |
| exit 1 | |
| fi | |
| fi | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Summary | |
| if: steps.detect-changes.outputs.has_changes == 'true' | |
| run: | | |
| CHALLENGE_COUNT=$(wc -l < /tmp/changed_challenges.txt) | |
| echo "## 📊 Scoreboards Updated" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Successfully updated scoreboards for **$CHALLENGE_COUNT** challenge(s):" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| while IFS= read -r challenge_dir; do | |
| [ -n "$challenge_dir" ] || continue | |
| echo "- $challenge_dir" >> $GITHUB_STEP_SUMMARY | |
| done < /tmp/changed_challenges.txt |