diff --git a/.github/workflows/build-error-analyzer.yml b/.github/workflows/build-error-analyzer.yml new file mode 100644 index 0000000..26cb7b4 --- /dev/null +++ b/.github/workflows/build-error-analyzer.yml @@ -0,0 +1,234 @@ +name: Build Error Analysis Report + +on: + # Trigger on workflow completions for better artifact handling + workflow_run: + workflows: ["Create New Release"] + types: + - completed + + # Also trigger directly on pull requests + pull_request: + types: [opened, synchronize, reopened] + +jobs: + # Job for analyzing artifacts from a failed workflow + analyze_workflow_artifacts: + name: Analyze Workflow Artifacts + runs-on: macos-15 + if: github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'failure' + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.workflow_run.head_branch }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install Git LFS + run: | + git lfs install + git lfs pull + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Download Workflow Artifacts + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + + // List artifacts from the failed workflow + const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{ github.event.workflow_run.id }} + }); + + console.log(`Found ${artifacts.data.artifacts.length} artifacts`); + + // Download all artifacts to analyze + for (const artifact of artifacts.data.artifacts) { + console.log(`Downloading: ${artifact.name} (${artifact.id})`); + + const download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: artifact.id, + archive_format: 'zip' + }); + + // Save with artifact name to keep track of everything + const filename = `${artifact.name.replace(/[ +^ +a-zA-Z0-9]/g, '_')}.zip`; + fs.writeFileSync(filename, Buffer.from(download.data)); + console.log(`Saved to ${filename}`); + } + + - name: Process Artifacts and Find Build Logs + id: process_artifacts + run: | + # Make artifact manager script executable + chmod +x scripts/ci/artifact-manager.sh + + # Process artifacts to find build logs + ./scripts/ci/artifact-manager.sh + + # Check if a valid build log was found + if [ -f "build_log.txt" ] && [ -s "build_log.txt" ]; then + echo "build_log_found=true" >> $GITHUB_OUTPUT + echo "✅ Build log found and ready for analysis" + else + echo "build_log_found=false" >> $GITHUB_OUTPUT + echo "⚠️ No valid build logs found in artifacts" + fi + + - name: Analyze Build Errors + if: steps.process_artifacts.outputs.build_log_found == 'true' + id: analysis + run: | + # Run the error analysis script + python3 scripts/ci/auto-fix-build-errors.py build_log.txt || echo "Analysis completed with warnings" + + # Check if reports were generated + if [ -f "build_error_report.html" ]; then + echo "report_generated=true" >> $GITHUB_OUTPUT + echo "✅ Build error report generated successfully" + else + echo "report_generated=false" >> $GITHUB_OUTPUT + echo "⚠️ No error report was generated" + fi + + - name: Upload Error Reports + if: steps.analysis.outputs.report_generated == 'true' + uses: actions/upload-artifact@v4 + with: + name: build-error-reports + path: | + build_error_report.html + build_error_report.txt + build_error_report.json + build-logs/ + retention-days: 30 + + - name: Generate Summary + if: steps.analysis.outputs.report_generated == 'true' + run: | + # Use script to generate GitHub step summary + ./scripts/ci/generate-report-summary.sh + + - name: Determine PR Number + id: pr-finder + if: steps.analysis.outputs.report_generated == 'true' + uses: actions/github-script@v7 + with: + script: | + // Get PR number from the workflow run + const run = await github.rest.actions.getWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{ github.event.workflow_run.id }} + }); + + // Extract PR number from the run data + const prNumber = run.data.pull_requests[0]?.number; + if (prNumber) { + console.log(`Found PR number: ${prNumber}`); + return prNumber; + } else { + console.log("Could not determine PR number from workflow run"); + return ''; + } + result-encoding: string + + - name: Create Comment on PR + if: steps.analysis.outputs.report_generated == 'true' && steps.pr-finder.outputs.result != '' + run: | + # Use script to create PR comment + ./scripts/ci/create-pr-comment.sh "${{ steps.pr-finder.outputs.result }}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Direct analysis job for pull requests + analyze_pr_build: + name: Analyze PR Build + runs-on: macos-15 + if: github.event_name == 'pull_request' + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Build Project for Analysis + id: build + continue-on-error: true + run: | + # Create a logs directory + mkdir -p build-logs + + # Try to build the project, capturing output to a log file + set +e + xcodebuild -project backdoor.xcodeproj -scheme "backdoor (Release)" -configuration Release CODE_SIGNING_ALLOWED=NO | tee build-logs/xcodebuild.log + BUILD_RESULT=$? + set -e + + # Check if build failed (we want to analyze failures) + if [ $BUILD_RESULT -ne 0 ]; then + echo "build_failed=true" >> $GITHUB_OUTPUT + echo "✅ Build failed as expected, logs captured for analysis" + else + echo "build_failed=false" >> $GITHUB_OUTPUT + echo "Build succeeded, no errors to analyze" + fi + + # Always link the log file to the expected location + cp build-logs/xcodebuild.log build_log.txt + + - name: Analyze Build Errors + if: steps.build.outputs.build_failed == 'true' + id: analysis + run: | + # Run the error analysis script + python3 scripts/ci/auto-fix-build-errors.py build_log.txt || echo "Analysis completed with warnings" + + # Check if reports were generated + if [ -f "build_error_report.html" ]; then + echo "report_generated=true" >> $GITHUB_OUTPUT + echo "✅ Build error report generated successfully" + else + echo "report_generated=false" >> $GITHUB_OUTPUT + echo "⚠️ No error report was generated" + fi + + - name: Upload Error Reports + if: steps.build.outputs.build_failed == 'true' && steps.analysis.outputs.report_generated == 'true' + uses: actions/upload-artifact@v4 + with: + name: pr-build-error-reports + path: | + build_error_report.html + build_error_report.txt + build_error_report.json + build-logs/ + retention-days: 30 + + - name: Generate Summary + if: steps.build.outputs.build_failed == 'true' && steps.analysis.outputs.report_generated == 'true' + run: | + # Use script to generate GitHub step summary + ./scripts/ci/generate-report-summary.sh + + - name: Create Comment on PR + if: steps.build.outputs.build_failed == 'true' && steps.analysis.outputs.report_generated == 'true' + run: | + # Use script to create PR comment + ./scripts/ci/create-pr-comment.sh "${{ github.event.pull_request.number }}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/scripts/ci/artifact-manager.sh b/scripts/ci/artifact-manager.sh new file mode 100755 index 0000000..f5cd5c4 --- /dev/null +++ b/scripts/ci/artifact-manager.sh @@ -0,0 +1,122 @@ +#!/bin/bash +set -eo pipefail + +# Focused Artifact Manager Script +# Prioritizes finding and analyzing build logs from artifacts + +echo "📦 Artifact Manager: Finding build logs in artifacts" + +# Create directories for organized processing +mkdir -p artifact-extracts +mkdir -p build-logs + +# Find all artifacts in the current directory and artifact-contents +find_artifacts() { + echo "🔍 Searching for artifact files..." + + # Common locations where artifacts might be found in GitHub Actions + ARTIFACT_DIRS=( + "." + "artifact-contents" + "artifacts" + "downloads" + "/home/runner/work/_temp" + ) + + # Look for zip files in these directories + for DIR in "${ARTIFACT_DIRS[@]}"; do + if [ -d "$DIR" ]; then + echo "Searching in $DIR" + find "$DIR" -name "*.zip" -type f | while read -r ZIP_FILE; do + echo "Found artifact: $ZIP_FILE" + extract_artifact "$ZIP_FILE" + done + fi + done +} + +# Extract an artifact file +extract_artifact() { + ZIP_FILE="$1" + EXTRACT_DIR="artifact-extracts/$(basename "$ZIP_FILE" .zip)" + + echo "Extracting $ZIP_FILE to $EXTRACT_DIR" + mkdir -p "$EXTRACT_DIR" + unzip -q -o "$ZIP_FILE" -d "$EXTRACT_DIR" || echo "Warning: Extraction issues with $ZIP_FILE" + + # Look for build logs in the extracted content + find_logs_in_extract "$EXTRACT_DIR" +} + +# Find log files in an extracted artifact +find_logs_in_extract() { + EXTRACT_DIR="$1" + echo "Searching for logs in $EXTRACT_DIR" + + # Common build log patterns + LOG_PATTERNS=( + "*build*.log" + "*build*.txt" + "*xcodebuild*.log" + "*output*.log" + "*compile*.log" + "*.build.log" + "*.txt" + ) + + # Find all potential log files + for PATTERN in "${LOG_PATTERNS[@]}"; do + find "$EXTRACT_DIR" -type f -iname "$PATTERN" 2>/dev/null | while read -r LOG_FILE; do + echo "Checking potential log: $LOG_FILE" + + # Check if this looks like a build log (contains error/warning messages) + if grep -q -E "error:|warning:|fatal error:|linker command failed|swift|xcodebuild" "$LOG_FILE"; then + echo "✅ Found build log: $LOG_FILE" + cp "$LOG_FILE" "build-logs/$(basename "$LOG_FILE")" + fi + done + done +} + +# Combine all found logs into a single file for analysis +combine_logs() { + echo "Combining logs for analysis..." + + if [ -z "$(ls -A build-logs 2>/dev/null)" ]; then + echo "::warning::No build logs found in artifacts" + echo "No build log found. This is a placeholder." > build_log.txt + return 1 + fi + + # Combine all logs with headers separating them + > combined_build_log.txt + for LOG_FILE in build-logs/*; do + echo "=== $(basename "$LOG_FILE") ===" >> combined_build_log.txt + echo "" >> combined_build_log.txt + cat "$LOG_FILE" >> combined_build_log.txt + echo "" >> combined_build_log.txt + echo "=== END OF $(basename "$LOG_FILE") ===" >> combined_build_log.txt + echo "" >> combined_build_log.txt + done + + # Create the main build log file for backward compatibility + cp combined_build_log.txt build_log.txt + echo "Created combined log file: build_log.txt" + + # Create a manifest of log files + find build-logs -type f | sort > build-logs/log_manifest.txt + echo "Created log manifest: build-logs/log_manifest.txt" + + return 0 +} + +# Main execution flow +find_artifacts +combine_logs + +# Summary +echo "✅ Artifact processing complete" +if [ -f "build_log.txt" ]; then + echo "Log files ready for analysis: build_log.txt" + echo "Individual logs available in: build-logs/" +fi diff --git a/scripts/ci/download-artifacts.sh b/scripts/ci/download-artifacts.sh index df7dee8..2e42a69 100755 --- a/scripts/ci/download-artifacts.sh +++ b/scripts/ci/download-artifacts.sh @@ -1,10 +1,10 @@ #!/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 +# Enhanced Artifact Downloader Script +# Downloads workflow artifacts and finds build logs with improved fallback mechanisms -echo "📥 Starting artifact download process..." +echo "📦 Enhanced Artifact Handler: Finding and processing build logs" # Validate required inputs WORKFLOW_RUN_ID="$1" @@ -13,11 +13,18 @@ if [ -z "$WORKFLOW_RUN_ID" ]; then exit 1 fi -# Create a directory for the Node.js script -mkdir -p temp -cat > temp/download-artifacts.js << 'EOL' +# Create directories for organized processing +mkdir -p artifact-extracts +mkdir -p build-logs + +# Download artifacts using GitHub API +download_artifacts() { + echo "📥 Downloading artifacts for workflow run: $WORKFLOW_RUN_ID" + + # Create a temporary script to download artifacts + mkdir -p temp + cat > temp/download-artifacts.js << 'EOL' const fs = require('fs'); -const path = require('path'); async function downloadArtifacts() { try { @@ -44,42 +51,33 @@ async function downloadArtifacts() { 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"); + if (!artifactsList.artifacts.length) { + console.log("No artifacts found for this workflow run"); 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'); + console.log(`Found ${artifactsList.artifacts.length} artifacts`); - // 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)); + // Download all artifacts to analyze + for (const artifact of artifactsList.artifacts) { + console.log(`Downloading: ${artifact.name} (${artifact.id})`); + + const download = await octokit.actions.downloadArtifact({ + owner, + repo, + artifact_id: artifact.id, + archive_format: 'zip' + }); + + // Save with artifact name to keep track of everything + const filename = `${artifact.name.replace(/[ +^ +a-zA-Z0-9]/g, '_')}.zip`; + fs.writeFileSync(filename, Buffer.from(download.data)); + console.log(`Saved to ${filename}`); + } - console.log('Successfully created artifact manifest'); + console.log('All artifacts downloaded successfully'); process.exit(0); } catch (error) { console.error('Error downloading artifacts:', error.message); @@ -90,53 +88,116 @@ async function downloadArtifacts() { downloadArtifacts(); EOL -# Install octokit -npm install @octokit/rest + # Install octokit if needed + npm install @octokit/rest -# Set environment variables and run the script -export WORKFLOW_RUN_ID="$WORKFLOW_RUN_ID" -export GITHUB_TOKEN="${GITHUB_TOKEN}" + # 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 + echo "Running download script..." + node temp/download-artifacts.js } -# List extracted contents -echo "Extracted artifact contents:" -ls -la artifact-contents +# Extract all artifacts and find build logs +process_artifacts() { + echo "📂 Processing artifacts and searching for build logs..." + + # Find all artifacts + ARTIFACT_ZIPS=$(find . -name "*.zip" -type f) + + if [ -z "$ARTIFACT_ZIPS" ]; then + echo "::warning::No artifact ZIP files found" + return 1 + fi + + # Extract each artifact to its own directory + for ZIP_FILE in $ARTIFACT_ZIPS; do + ARTIFACT_NAME=$(basename "$ZIP_FILE" .zip) + EXTRACT_DIR="artifact-extracts/$ARTIFACT_NAME" + + echo "Extracting $ZIP_FILE to $EXTRACT_DIR" + mkdir -p "$EXTRACT_DIR" + unzip -q -o "$ZIP_FILE" -d "$EXTRACT_DIR" || echo "Warning: Extraction issues with $ZIP_FILE" + + # Look for build logs in the extracted content + find "$EXTRACT_DIR" -type f -name "*.log" -o -name "*.txt" | while read -r LOG_FILE; do + echo "Checking potential log: $LOG_FILE" + + # Check if this looks like a build log + if grep -q -E "error:|warning:|fatal error:|linker command failed|swift|xcodebuild" "$LOG_FILE"; then + echo "✅ Found build log: $LOG_FILE" + cp "$LOG_FILE" "build-logs/$(basename "$LOG_FILE")" + fi + done + done + + # Also check the main artifact-contents directory if it exists + if [ -d "artifact-contents" ]; then + echo "Checking artifact-contents directory..." + + # Look for build logs there too + find "artifact-contents" -type f -name "*.log" -o -name "*.txt" | while read -r LOG_FILE; do + if grep -q -E "error:|warning:|fatal error:|linker command failed|swift|xcodebuild" "$LOG_FILE"; then + echo "✅ Found build log in artifact-contents: $LOG_FILE" + cp "$LOG_FILE" "build-logs/$(basename "$LOG_FILE")" + fi + done + fi +} -# 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 +# Combine logs into a single file +combine_logs() { + echo "Combining logs for analysis..." + + if [ -z "$(ls -A build-logs 2>/dev/null)" ]; then + echo "::warning::No build logs found in artifacts" + + # Look for build logs in original location as fallback + if [ -f "artifact-contents/build_log.txt" ]; then + echo "Using original build_log.txt as fallback" + cp artifact-contents/build_log.txt ./build_log.txt + return 0 + elif [ -f "artifact-contents/xcodebuild.log" ]; then + echo "Using xcodebuild.log as fallback" + cp artifact-contents/xcodebuild.log ./build_log.txt + return 0 + else + # Create an empty log file to prevent errors in subsequent steps + echo "No build log found. This is a placeholder." > build_log.txt + return 1 + fi fi -fi + + # Combine all logs with headers separating them + > combined_build_log.txt + for LOG_FILE in build-logs/*; do + echo "=== $(basename "$LOG_FILE") ===" >> combined_build_log.txt + echo "" >> combined_build_log.txt + cat "$LOG_FILE" >> combined_build_log.txt + echo "" >> combined_build_log.txt + echo "=== END OF $(basename "$LOG_FILE") ===" >> combined_build_log.txt + echo "" >> combined_build_log.txt + done + + # Create the main build log file for backward compatibility + cp combined_build_log.txt build_log.txt + echo "Created combined log file: build_log.txt" + + # Create a manifest of log files + find build-logs -type f | sort > build-logs/log_manifest.txt + echo "Created log manifest: build-logs/log_manifest.txt" + + return 0 +} + +# Main execution flow +download_artifacts +process_artifacts +combine_logs # Clean up temporary files rm -rf temp -echo "✅ Artifact processing complete" +echo "✅ Enhanced artifact processing complete" +echo "Log files ready for analysis: build_log.txt" diff --git a/scripts/ci/download-artifacts.sh.bak b/scripts/ci/download-artifacts.sh.bak new file mode 100755 index 0000000..df7dee8 --- /dev/null +++ b/scripts/ci/download-artifacts.sh.bak @@ -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/workflows/analyze-errors.sh b/scripts/ci/workflows/analyze-errors.sh new file mode 100755 index 0000000..470dfa4 --- /dev/null +++ b/scripts/ci/workflows/analyze-errors.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -eo pipefail + +# Script to run the error analysis on build logs +# Usage: ./analyze-errors.sh [log_file] + +LOG_FILE="${1:-build_log.txt}" + +echo "Analyzing build errors in $LOG_FILE..." + +# Run the Python error analyzer +python3 scripts/ci/auto-fix-build-errors.py "$LOG_FILE" || echo "Analysis completed with warnings" + +# Check if reports were generated +if [ -f "build_error_report.html" ]; then + echo "report_generated=true" >> $GITHUB_OUTPUT + echo "✅ Build error report generated successfully" +else + echo "report_generated=false" >> $GITHUB_OUTPUT + echo "⚠️ No error report was generated" +fi + +echo "✅ Error analysis complete" diff --git a/scripts/ci/workflows/build-project.sh b/scripts/ci/workflows/build-project.sh new file mode 100755 index 0000000..027c731 --- /dev/null +++ b/scripts/ci/workflows/build-project.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -eo pipefail + +# Script to build the project and capture logs for error analysis +# Usage: ./build-project.sh + +echo "Building project for error analysis..." + +# Create logs directory +mkdir -p build-logs + +# Run the build with output capture +echo "Running xcodebuild..." +set +e +xcodebuild -project backdoor.xcodeproj -scheme "backdoor (Release)" -configuration Release CODE_SIGNING_ALLOWED=NO | tee build-logs/xcodebuild.log +BUILD_RESULT=$? +set -e + +# Check build result +if [ $BUILD_RESULT -ne 0 ]; then + echo "build_failed=true" >> $GITHUB_OUTPUT + echo "✅ Build failed as expected, logs captured for analysis" +else + echo "build_failed=false" >> $GITHUB_OUTPUT + echo "Build succeeded, no errors to analyze" +fi + +# Always link the log to the expected location +cp build-logs/xcodebuild.log build_log.txt + +echo "✅ Build process complete" diff --git a/scripts/ci/workflows/check-build-logs.sh b/scripts/ci/workflows/check-build-logs.sh new file mode 100755 index 0000000..bff8d37 --- /dev/null +++ b/scripts/ci/workflows/check-build-logs.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -eo pipefail + +# Script to check if build logs were found and set GitHub outputs +# Usage: ./check-build-logs.sh + +# Look for build logs in the expected location +if [ -f "build_log.txt" ] && [ -s "build_log.txt" ]; then + echo "build_log_found=true" >> $GITHUB_OUTPUT + echo "✅ Build log found and ready for analysis" +else + echo "build_log_found=false" >> $GITHUB_OUTPUT + echo "⚠️ No valid build logs found in artifacts" + + # Create the output directory if it doesn't exist + mkdir -p build-logs + + # Look for any potential log files + FOUND_LOGS=$(find . -type f -name "*.log" -o -name "*.txt") + + if [ -n "$FOUND_LOGS" ]; then + echo "Found potential log files:" + echo "$FOUND_LOGS" + + # Copy the first log file found as a fallback + FIRST_LOG=$(echo "$FOUND_LOGS" | head -n 1) + if [ -n "$FIRST_LOG" ]; then + echo "Using $FIRST_LOG as fallback log" + cp "$FIRST_LOG" "build_log.txt" + echo "build_log_found=true" >> $GITHUB_OUTPUT + fi + fi +fi diff --git a/scripts/ci/workflows/download-workflow-artifacts.sh b/scripts/ci/workflows/download-workflow-artifacts.sh new file mode 100755 index 0000000..26f6a83 --- /dev/null +++ b/scripts/ci/workflows/download-workflow-artifacts.sh @@ -0,0 +1,95 @@ +#!/bin/bash +set -eo pipefail + +# Script to download artifacts from a GitHub workflow run +# Usage: ./download-workflow-artifacts.sh WORKFLOW_RUN_ID + +WORKFLOW_RUN_ID="$1" +if [ -z "$WORKFLOW_RUN_ID" ]; then + echo "::error::No workflow run ID provided" + exit 1 +fi + +echo "Downloading artifacts for workflow run: $WORKFLOW_RUN_ID" + +# Create a temporary script to download artifacts using GitHub API +mkdir -p temp +cat > temp/download-artifacts.js << 'EOL' +const fs = require('fs'); + +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 + }); + + if (!artifactsList.artifacts.length) { + console.log("No artifacts found for this workflow run"); + process.exit(0); + } + + console.log(`Found ${artifactsList.artifacts.length} artifacts`); + + // Download all artifacts + for (const artifact of artifactsList.artifacts) { + console.log(`Downloading: ${artifact.name} (${artifact.id})`); + + const download = await octokit.actions.downloadArtifact({ + owner, + repo, + artifact_id: artifact.id, + archive_format: 'zip' + }); + + // Save with artifact name to keep track of everything + const filename = `${artifact.name.replace(/[ +^ +a-zA-Z0-9]/g, '_')}.zip`; + fs.writeFileSync(filename, Buffer.from(download.data)); + console.log(`Saved to ${filename}`); + } + + console.log('All artifacts downloaded successfully'); + process.exit(0); + } catch (error) { + console.error('Error downloading artifacts:', error.message); + process.exit(1); + } +} + +downloadArtifacts(); +EOL + +# Install octokit if needed +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 + +# Clean up temporary files +rm -rf temp + +echo "✅ Artifact download complete" diff --git a/scripts/ci/workflows/find-pr-number.sh b/scripts/ci/workflows/find-pr-number.sh new file mode 100755 index 0000000..ae9c880 --- /dev/null +++ b/scripts/ci/workflows/find-pr-number.sh @@ -0,0 +1,86 @@ +#!/bin/bash +set -eo pipefail + +# Script to find PR number from a workflow run +# Usage: ./find-pr-number.sh WORKFLOW_RUN_ID + +WORKFLOW_RUN_ID="$1" +if [ -z "$WORKFLOW_RUN_ID" ]; then + echo "::error::No workflow run ID provided" + exit 1 +fi + +echo "Finding PR number for workflow run: $WORKFLOW_RUN_ID" + +# Create temporary script to find PR number +mkdir -p temp +cat > temp/find-pr.js << 'EOL' +const fs = require('fs'); + +async function findPrNumber() { + 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); + } + + // Create the Octokit client + const { Octokit } = require('@octokit/rest'); + const octokit = new Octokit({ auth: token }); + + // Get the workflow run + const run = await octokit.actions.getWorkflowRun({ + owner, + repo, + run_id: workflowRunId + }); + + // Extract PR number from the run data + const prNumber = run.data.pull_requests[0]?.number; + if (prNumber) { + console.log(`Found PR number: ${prNumber}`); + // Write the PR number to a file for the bash script to read + fs.writeFileSync('pr_number.txt', prNumber.toString()); + process.exit(0); + } else { + console.log("Could not determine PR number from workflow run"); + process.exit(1); + } + } catch (error) { + console.error('Error finding PR number:', error.message); + process.exit(1); + } +} + +findPrNumber(); +EOL + +# Install octokit if needed +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 PR finder script..." +node temp/find-pr.js + +# Read PR number from file +if [ -f "pr_number.txt" ]; then + PR_NUMBER=$(cat pr_number.txt) + echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + echo "✅ Found PR #$PR_NUMBER" +else + echo "pr_number=" >> $GITHUB_OUTPUT + echo "⚠️ No PR number found" +fi + +# Clean up temporary files +rm -rf temp pr_number.txt + +echo "✅ PR number search complete"