feat: add cross-platform GUI desktop app (Avalonia) with drag-drop, b… #193
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: CI | |
| on: | |
| push: | |
| branches: [ main, master ] | |
| paths-ignore: | |
| - '**.md' | |
| - 'documents/**' | |
| - 'dotnet_ai_azure_book/**' | |
| - 'book/**' | |
| - 'MiniPdf.wiki/**' | |
| - 'LICENSE' | |
| pull_request: | |
| branches: [ main, master ] | |
| paths-ignore: | |
| - '**.md' | |
| - 'documents/**' | |
| - 'dotnet_ai_azure_book/**' | |
| - 'book/**' | |
| - 'MiniPdf.wiki/**' | |
| - 'LICENSE' | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '9.0.x' | |
| - name: Restore dependencies | |
| run: dotnet restore | |
| - name: Build | |
| run: dotnet build --no-restore --configuration Release | |
| - name: Test | |
| run: dotnet test --no-build --configuration Release --verbosity normal --logger "trx;LogFileName=test-results.trx" | |
| - name: Pack (verify NuGet package) | |
| run: dotnet pack src/MiniPdf/MiniPdf.csproj --no-build --configuration Release --output ./nupkg | |
| - name: Upload test results | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: test-results | |
| path: '**/test-results.trx' | |
| ai-security-scan: | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Get PR diff | |
| id: diff | |
| run: | | |
| DIFF=$(git diff origin/${{ github.base_ref }}...HEAD -- '*.cs' '*.csproj' || true) | |
| if [ -z "$DIFF" ]; then | |
| echo "No code changes detected." | |
| echo "skip=true" >> $GITHUB_OUTPUT | |
| else | |
| # Save diff to file to avoid shell escaping issues | |
| echo "$DIFF" > /tmp/pr_diff.txt | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: AI Security Review | |
| if: steps.diff.outputs.skip != 'true' | |
| env: | |
| AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }} | |
| AZURE_OPENAI_ENDPOINT: ${{ secrets.AZURE_OPENAI_ENDPOINT }} | |
| AZURE_OPENAI_DEPLOYMENT: ${{ secrets.AZURE_OPENAI_DEPLOYMENT }} | |
| run: | | |
| DIFF=$(cat /tmp/pr_diff.txt) | |
| # Truncate diff if too large (max ~12000 chars to fit in context) | |
| if [ ${#DIFF} -gt 12000 ]; then | |
| DIFF="${DIFF:0:12000}... [truncated]" | |
| fi | |
| INSTRUCTIONS=$(cat .github/copilot-code-review.md 2>/dev/null || echo "Review for security issues.") | |
| API_VERSION="2025-01-01-preview" | |
| URL="${AZURE_OPENAI_ENDPOINT%/}/openai/deployments/${AZURE_OPENAI_DEPLOYMENT}/chat/completions?api-version=${API_VERSION}" | |
| # Build JSON payload safely using jq | |
| PAYLOAD=$(jq -n \ | |
| --arg instructions "$INSTRUCTIONS" \ | |
| --arg diff "$DIFF" \ | |
| '{ | |
| messages: [ | |
| { role: "system", content: $instructions }, | |
| { role: "user", content: ("Review this code diff for security vulnerabilities in APPLICATION SOURCE CODE ONLY. Respond ONLY with a JSON object (no markdown, no code blocks): {\"passed\": true/false, \"issues\": [\"description1\", \"description2\"]}. Set passed=true if no security issues found in application code, passed=false only for real security concerns like SQL injection, XSS, path traversal, hardcoded credentials in source code, etc. Do NOT flag CI/CD workflow configuration or documentation changes as issues.\n\nDiff:\n" + $diff) } | |
| ], | |
| temperature: 0.1 | |
| }') | |
| RESPONSE=$(curl -s "$URL" \ | |
| -H "Content-Type: application/json" \ | |
| -H "api-key: $AZURE_OPENAI_API_KEY" \ | |
| -d "$PAYLOAD") | |
| # Extract content | |
| CONTENT=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // empty') | |
| if [ -z "$CONTENT" ]; then | |
| echo "::error::Failed to get AI review response" | |
| echo "$RESPONSE" | jq . | |
| exit 1 | |
| fi | |
| echo "=== AI Security Review Result ===" | |
| echo "$CONTENT" | |
| echo "=================================" | |
| # Parse JSON from response: strip markdown code blocks, then parse with jq | |
| CLEAN_CONTENT=$(echo "$CONTENT" | sed '/^```/d') | |
| # Use 'if .passed then "true" else "false" end' to handle boolean false correctly | |
| PASSED=$(echo "$CLEAN_CONTENT" | jq -r 'if .passed == true then "true" elif .passed == false then "false" else "unknown" end' 2>/dev/null) | |
| if [ "$PASSED" = "false" ]; then | |
| echo "" | |
| echo "::error::AI Security Review FAILED - security issues detected" | |
| echo "$CLEAN_CONTENT" | jq -r '.issues[]?' 2>/dev/null | while read -r issue; do | |
| echo "::warning::$issue" | |
| done | |
| exit 1 | |
| elif [ "$PASSED" = "true" ]; then | |
| echo "" | |
| echo "✅ AI Security Review PASSED - no security issues found" | |
| else | |
| echo "::warning::Could not parse AI review result, treating as FAIL for safety" | |
| exit 1 | |
| fi | |
| - name: Skip notice | |
| if: steps.diff.outputs.skip == 'true' | |
| run: echo "✅ No code changes to review" |