diff --git a/.github/workflows/auto-fix-build-errors.yml b/.github/workflows/auto-fix-build-errors.yml index 8c01f26..47857b9 100644 --- a/.github/workflows/auto-fix-build-errors.yml +++ b/.github/workflows/auto-fix-build-errors.yml @@ -23,56 +23,27 @@ jobs: git lfs install git lfs pull - - name: Download Artifacts - uses: actions/github-script@v7 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - script: | - const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: ${{ github.event.workflow_run.id }} - }); - - const buildLog = artifacts.data.artifacts.find(artifact => artifact.name === "ipa-files"); - if (!buildLog) { - console.log("No build log artifacts found."); - return; - } - - const download = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: buildLog.id, - archive_format: 'zip' - }); - - const fs = require('fs'); - fs.writeFileSync('artifact.zip', Buffer.from(download.data)); - - console.log("Downloaded artifact.zip"); + node-version: '20' - - name: Extract Artifacts + - name: Download Artifacts run: | - mkdir -p artifact-contents - unzip -o artifact.zip -d artifact-contents || echo "No artifacts to extract" - ls -la artifact-contents || echo "No artifact contents" + # Make scripts executable + chmod +x scripts/ci/*.sh - if [ -f "artifact-contents/build_log.txt" ]; then - echo "Build log found, copying for analysis..." - cp artifact-contents/build_log.txt ./ - else - echo "::warning::No build log found in artifacts" - fi - - - name: Install Python Dependencies - run: | - pip3 install html + # Download artifacts using the helper script + ./scripts/ci/download-artifacts.sh "${{ github.event.workflow_run.id }}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Run Build Error Analysis id: analysis run: | - chmod +x scripts/ci/auto-fix-build-errors.py + # No special Python libraries needed - using standard library only + # Run analysis script if [ -f "build_log.txt" ]; then echo "Analyzing build log..." python3 scripts/ci/auto-fix-build-errors.py build_log.txt || echo "Analysis completed with errors" @@ -105,45 +76,15 @@ jobs: - name: Generate Summary if: steps.analysis.outputs.report_generated == 'true' run: | - echo "## 📊 Build Error Analysis Report" > $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "A detailed analysis of the build errors has been generated." >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - # Extract summary information from the JSON report - if [ -f "build_error_report.json" ]; then - ERROR_COUNT=$(grep -o '"error_count":[0-9]*' build_error_report.json | cut -d ":" -f2) - WARNING_COUNT=$(grep -o '"warning_count":[0-9]*' build_error_report.json | cut -d ":" -f2) - - echo "### Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "* 🛑 **Errors**: ${ERROR_COUNT:-0}" >> $GITHUB_STEP_SUMMARY - echo "* ⚠️ **Warnings**: ${WARNING_COUNT:-0}" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - fi - - echo "### Common Issues" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - # Include top issues from the report - if [ -f "build_error_report.txt" ]; then - # Extract top error types - echo "#### Top Error Types" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - grep -A 5 "ERROR TYPES:" build_error_report.txt >> $GITHUB_STEP_SUMMARY || echo "No error types found" - echo '```' >> $GITHUB_STEP_SUMMARY - fi - - echo "" >> $GITHUB_STEP_SUMMARY - echo "Please download the full HTML report from the artifacts for a comprehensive analysis." >> $GITHUB_STEP_SUMMARY + # Use the dedicated script to generate the GitHub step summary + ./scripts/ci/generate-report-summary.sh - - name: Create Comment on PR + - name: Determine PR Number + id: pr-finder if: github.event.workflow_run.event == 'pull_request' && steps.analysis.outputs.report_generated == 'true' uses: actions/github-script@v7 with: script: | - const fs = require('fs'); - // Get PR number from the workflow run const run = await github.rest.actions.getWorkflowRun({ owner: context.repo.owner, @@ -153,43 +94,19 @@ jobs: // Extract PR number from the run data const prNumber = run.data.pull_requests[0]?.number; - if (!prNumber) { + if (prNumber) { + console.log(`Found PR number: ${prNumber}`); + return prNumber; + } else { console.log("Could not determine PR number from workflow run"); - return; + return ''; } - - // Read summary information from the JSON report - if (fs.existsSync('build_error_report.json')) { - const reportData = JSON.parse(fs.readFileSync('build_error_report.json', 'utf8')); - const errorCount = reportData.summary.error_count; - const warningCount = reportData.summary.warning_count; - - // Create a comment on the PR - const body = `## 🔍 Build Error Analysis Report - -This PR contains: -- 🛑 **${errorCount} errors** -- ⚠️ **${warningCount} warnings** - -
-Click to see error type breakdown + result-encoding: string -\`\`\` -${Object.entries(reportData.summary.error_types) - .map(([type, count]) => `${type}: ${count}`) - .join('\n')} -\`\`\` -
- -Please check the workflow run for the full HTML report with error details and suggestions for fixing them. - `; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body: body.replace(/^\s+/gm, '') // Remove leading spaces - }); - - console.log(`Created comment on PR #${prNumber}`); - } + - name: Create Comment on PR + if: github.event.workflow_run.event == 'pull_request' && steps.analysis.outputs.report_generated == 'true' && steps.pr-finder.outputs.result != '' + run: | + # Use dedicated script to create PR comment + ./scripts/ci/create-pr-comment.sh "${{ steps.pr-finder.outputs.result }}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/scripts/ci/create-pr-comment.sh b/scripts/ci/create-pr-comment.sh new file mode 100755 index 0000000..c0ad508 --- /dev/null +++ b/scripts/ci/create-pr-comment.sh @@ -0,0 +1,112 @@ +#!/bin/bash +set -eo pipefail + +# This script creates a PR comment with build error analysis +# It requires a GitHub token and PR number + +echo "💬 Creating PR comment with build error analysis..." + +# Validate input parameters +PR_NUMBER="$1" +if [ -z "$PR_NUMBER" ]; then + echo "::error::No PR number provided" + exit 1 +fi + +if [ ! -f "build_error_report.json" ]; then + echo "::error::No build error report found" + exit 1 +fi + +# Create a temporary directory for the Node.js script +mkdir -p temp +cat > temp/create-comment.js << 'EOL' +const fs = require('fs'); + +async function createComment() { + try { + // Parse inputs + const prNumber = process.env.PR_NUMBER; + const token = process.env.GITHUB_TOKEN; + const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/'); + + if (!prNumber || !token || !owner || !repo) { + console.error('Missing required environment variables'); + process.exit(1); + } + + console.log(`Creating comment on PR #${prNumber} in ${owner}/${repo}`); + + // Create the Octokit client + const { Octokit } = require('@octokit/rest'); + const octokit = new Octokit({ auth: token }); + + // Read the error report data + if (!fs.existsSync('build_error_report.json')) { + console.error('Error report JSON file not found'); + process.exit(1); + } + + const reportData = JSON.parse(fs.readFileSync('build_error_report.json', 'utf8')); + const errorCount = reportData.summary.error_count; + const warningCount = reportData.summary.warning_count; + + // Format error types for display + let errorTypesList = ''; + if (reportData.summary.error_types) { + errorTypesList = Object.entries(reportData.summary.error_types) + .map(([type, count]) => `${type}: ${count}`) + .join('\n'); + } + + // Create the comment body + const body = `## 🔍 Build Error Analysis Report + +This PR contains: +- 🛑 **${errorCount} errors** +- ⚠️ **${warningCount} warnings** + +
+Click to see error type breakdown + +\`\`\` +${errorTypesList} +\`\`\` +
+ +Please check the workflow run for the full HTML report with error details and suggestions for fixing them. +`; + + // Create the comment + await octokit.issues.createComment({ + owner, + repo, + issue_number: prNumber, + body + }); + + console.log(`Successfully created comment on PR #${prNumber}`); + process.exit(0); + } catch (error) { + console.error('Error creating PR comment:', error.message); + process.exit(1); + } +} + +createComment(); +EOL + +# Install octokit if not already installed +npm install @octokit/rest + +# Set environment variables and run the script +export PR_NUMBER="$PR_NUMBER" +export GITHUB_TOKEN="${GITHUB_TOKEN}" + +echo "Running comment creation script..." +node temp/create-comment.js + +# Clean up temporary files +rm -rf temp + +echo "✅ PR comment creation complete" diff --git a/scripts/ci/download-artifacts.sh b/scripts/ci/download-artifacts.sh new file mode 100755 index 0000000..df7dee8 --- /dev/null +++ b/scripts/ci/download-artifacts.sh @@ -0,0 +1,142 @@ +#!/bin/bash +set -eo pipefail + +# This script downloads and extracts artifacts from a failed workflow run +# It's designed to be called from the auto-fix-build-errors workflow + +echo "📥 Starting artifact download process..." + +# Validate required inputs +WORKFLOW_RUN_ID="$1" +if [ -z "$WORKFLOW_RUN_ID" ]; then + echo "::error::No workflow run ID provided" + exit 1 +fi + +# Create a directory for the Node.js script +mkdir -p temp +cat > temp/download-artifacts.js << 'EOL' +const fs = require('fs'); +const path = require('path'); + +async function downloadArtifacts() { + try { + // Parse inputs + const workflowRunId = process.env.WORKFLOW_RUN_ID; + const token = process.env.GITHUB_TOKEN; + const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/'); + + if (!workflowRunId || !token || !owner || !repo) { + console.error('Missing required environment variables'); + process.exit(1); + } + + console.log(`Downloading artifacts for workflow run ${workflowRunId} in ${owner}/${repo}`); + + // Create the Octokit client + const { Octokit } = require('@octokit/rest'); + const octokit = new Octokit({ auth: token }); + + // List artifacts + const { data: artifactsList } = await octokit.actions.listWorkflowRunArtifacts({ + owner, + repo, + run_id: workflowRunId + }); + + console.log(`Found ${artifactsList.artifacts.length} artifacts`); + + // Find the build log artifact + const buildLog = artifactsList.artifacts.find(artifact => + artifact.name === "ipa-files" || + artifact.name.includes("build") || + artifact.name.includes("log") + ); + + if (!buildLog) { + console.log("No build log artifacts found"); + process.exit(0); + } + + console.log(`Found build log artifact: ${buildLog.name} (${buildLog.id})`); + + // Download the artifact + const download = await octokit.actions.downloadArtifact({ + owner, + repo, + artifact_id: buildLog.id, + archive_format: 'zip' + }); + + // Save the zip file + fs.writeFileSync('artifact.zip', Buffer.from(download.data)); + console.log('Artifact downloaded successfully to artifact.zip'); + + // Create a manifest file with artifact details + fs.writeFileSync('artifact-manifest.json', JSON.stringify({ + artifact_id: buildLog.id, + artifact_name: buildLog.name, + workflow_run_id: workflowRunId + }, null, 2)); + + console.log('Successfully created artifact manifest'); + process.exit(0); + } catch (error) { + console.error('Error downloading artifacts:', error.message); + process.exit(1); + } +} + +downloadArtifacts(); +EOL + +# Install octokit +npm install @octokit/rest + +# Set environment variables and run the script +export WORKFLOW_RUN_ID="$WORKFLOW_RUN_ID" +export GITHUB_TOKEN="${GITHUB_TOKEN}" + +echo "Running download script..." +node temp/download-artifacts.js + +# Extract the artifacts +echo "Extracting artifacts..." +mkdir -p artifact-contents +unzip -o artifact.zip -d artifact-contents || { + echo "::warning::Failed to extract artifacts, possibly empty or corrupt" + ls -la + exit 0 +} + +# List extracted contents +echo "Extracted artifact contents:" +ls -la artifact-contents + +# Look for build logs - try multiple common names +if [ -f "artifact-contents/build_log.txt" ]; then + echo "Build log found, copying for analysis..." + cp artifact-contents/build_log.txt ./ +elif [ -f "artifact-contents/buildlog.txt" ]; then + echo "Build log found as buildlog.txt, copying for analysis..." + cp artifact-contents/buildlog.txt ./build_log.txt +elif [ -f "artifact-contents/xcodebuild.log" ]; then + echo "Build log found as xcodebuild.log, copying for analysis..." + cp artifact-contents/xcodebuild.log ./build_log.txt +else + # If we can't find a specific log file, look for any text file + TEXT_FILE=$(find artifact-contents -name "*.txt" -or -name "*.log" | head -n 1) + if [ -n "$TEXT_FILE" ]; then + echo "Using $TEXT_FILE as build log..." + cp "$TEXT_FILE" ./build_log.txt + else + echo "::warning::No build log found in artifacts" + # Create an empty log file to prevent errors in subsequent steps + echo "No build log found. This is a placeholder." > build_log.txt + fi +fi + +# Clean up temporary files +rm -rf temp + +echo "✅ Artifact processing complete" diff --git a/scripts/ci/generate-report-summary.sh b/scripts/ci/generate-report-summary.sh new file mode 100755 index 0000000..e720516 --- /dev/null +++ b/scripts/ci/generate-report-summary.sh @@ -0,0 +1,77 @@ +#!/bin/bash +set -eo pipefail + +# This script generates a GitHub Actions workflow summary from build error reports +# It's designed to be called from the auto-fix-build-errors workflow + +echo "📝 Generating build error report summary..." + +# Create the summary markdown file +echo "## 📊 Build Error Analysis Report" > $GITHUB_STEP_SUMMARY +echo "" >> $GITHUB_STEP_SUMMARY +echo "A detailed analysis of the build errors has been generated." >> $GITHUB_STEP_SUMMARY +echo "" >> $GITHUB_STEP_SUMMARY + +# Extract summary information from the JSON report +if [ -f "build_error_report.json" ]; then + ERROR_COUNT=$(grep -o '"error_count":[0-9]*' build_error_report.json | cut -d ":" -f2) + WARNING_COUNT=$(grep -o '"warning_count":[0-9]*' build_error_report.json | cut -d ":" -f2) + + echo "### Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "* 🛑 **Errors**: ${ERROR_COUNT:-0}" >> $GITHUB_STEP_SUMMARY + echo "* ⚠️ **Warnings**: ${WARNING_COUNT:-0}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Extract error types if available + ERROR_TYPES_JSON=$(grep -o '"error_types":{[ +^ +}]*}' build_error_report.json) + if [ -n "$ERROR_TYPES_JSON" ]; then + echo "### Error Types" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "```" >> $GITHUB_STEP_SUMMARY + # Format the JSON for better readability + echo "$ERROR_TYPES_JSON" | sed 's/"error_types"://' | sed 's/{//' | sed 's/}//' | sed 's/,/\n/g' | sed 's/"//g' | sed 's/:/: /g' >> $GITHUB_STEP_SUMMARY + echo "```" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + fi +else + echo "### Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "No detailed error report was generated." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY +fi + +# Include top issues from the text report +if [ -f "build_error_report.txt" ]; then + echo "### Common Issues" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Extract top error types + ERROR_TYPES_SECTION=$(grep -A 10 "ERROR TYPES:" build_error_report.txt) + if [ -n "$ERROR_TYPES_SECTION" ]; then + echo "```" >> $GITHUB_STEP_SUMMARY + echo "$ERROR_TYPES_SECTION" >> $GITHUB_STEP_SUMMARY + echo "```" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + fi + + # Extract a few example errors for quick reference + echo "### Example Errors" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "```" >> $GITHUB_STEP_SUMMARY + grep -A 2 "ERROR:" build_error_report.txt | head -n 9 >> $GITHUB_STEP_SUMMARY + echo "```" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY +fi + +echo "📦 Artifact Information" >> $GITHUB_STEP_SUMMARY +echo "" >> $GITHUB_STEP_SUMMARY +echo "A detailed HTML report has been uploaded as an artifact." >> $GITHUB_STEP_SUMMARY +echo "Download it from the workflow run page for a more comprehensive analysis." >> $GITHUB_STEP_SUMMARY + +echo "✅ Report summary generation complete" + +# Output success so CI knows we completed successfully +exit 0