diff --git a/action.yml b/action.yml index 4262137..9025bb6 100644 --- a/action.yml +++ b/action.yml @@ -25,6 +25,10 @@ inputs: build-delay: description: Time to wait for docker container build default: 50s + github-token: + description: GitHub token for PR interactions + required: false + default: ${{ github.token }} runs: using: "composite" @@ -34,32 +38,61 @@ runs: echo "${{ github.action_path }}" >> $GITHUB_PATH echo "${{ inputs.working-directory }}" shell: bash - - name: Grant permissions - run: chmod +x ${GITHUB_ACTION_PATH}/install.sh + + - name: Grant permissions to scripts + run: | + chmod +x ${GITHUB_ACTION_PATH}/install.sh ${GITHUB_ACTION_PATH}/detect-languages.sh ${GITHUB_ACTION_PATH}/analyze-code.sh ${GITHUB_ACTION_PATH}/parse-results.sh shell: bash + - id: keploy-test-report - name: Run Script - run: | - ${GITHUB_ACTION_PATH}/install.sh > ${GITHUB_WORKSPACE}/${WORKDIR}/report.txt - cat ${GITHUB_WORKSPACE}/${WORKDIR}/report.txt - - # Search for the complete testrun summary and extract total tests, total test passed, and total test failed data - grep -oE "COMPLETE TESTRUN SUMMARY\.\s+Total tests: [0-9]+" ${GITHUB_WORKSPACE}/${WORKDIR}/report.txt | sed -r "s/\x1B\[[0-9;]*[mGK]//g" > ${GITHUB_WORKSPACE}/${WORKDIR}/final_total_tests.out - grep -oE "COMPLETE TESTRUN SUMMARY\.\s+Total test passed: [0-9]+" ${GITHUB_WORKSPACE}/${WORKDIR}/report.txt | sed -r "s/\x1B\[[0-9;]*[mGK]//g" > ${GITHUB_WORKSPACE}/${WORKDIR}/final_total_passed.out - grep -oE "COMPLETE TESTRUN SUMMARY\.\s+Total test failed: [0-9]+" ${GITHUB_WORKSPACE}/${WORKDIR}/report.txt | sed -r "s/\x1B\[[0-9;]*[mGK]//g" > ${GITHUB_WORKSPACE}/${WORKDIR}/final_total_failed.out - - # Combine the results into a single file and prepare output - cat ${GITHUB_WORKSPACE}/${WORKDIR}/final_total_tests.out ${GITHUB_WORKSPACE}/${WORKDIR}/final_total_passed.out ${GITHUB_WORKSPACE}/${WORKDIR}/final_total_failed.out > ${GITHUB_WORKSPACE}/${WORKDIR}/final.out - echo 'KEPLOY_REPORT< $GITHUB_OUTPUT - cat ${GITHUB_WORKSPACE}/${WORKDIR}/final.out >> $GITHUB_OUTPUT - echo 'EOF' >> $GITHUB_OUTPUT - cat $GITHUB_OUTPUT + name: Run Keploy Tests + run: ${GITHUB_ACTION_PATH}/install.sh > ${GITHUB_WORKSPACE}/${WORKDIR}/report.txt shell: bash env: WORKDIR: ${{ inputs.working-directory }} DELAY: ${{ inputs.delay }} - COMMAND : ${{ inputs.command }} + COMMAND: ${{ inputs.command }} KEPLOY_PATH: ${{inputs.keploy-path}} + + - name: Parse Test Results + id: parse-results + run: ${GITHUB_ACTION_PATH}/parse-results.sh + shell: bash + env: + WORKDIR: ${{ inputs.working-directory }} + + - name: Fetch PR changes + id: pr-files + uses: actions/github-script@v6 + with: + github-token: ${{ inputs.github-token }} + script: | + try { + const response = await github.rest.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + const files = response.data.map(file => file.filename); + core.setOutput('files', files.join(',')); + return files; + } catch (error) { + core.setFailed(`Failed to fetch PR files: ${error.message}`); + } + + - name: Detect languages + id: language-detection + run: ${GITHUB_ACTION_PATH}/detect-languages.sh + shell: bash + + - name: Run code analysis + run: ${GITHUB_ACTION_PATH}/analyze-code.sh + shell: bash + env: + WORKDIR: ${{ inputs.working-directory }} + DETECTED_LANGUAGES: ${{ steps.language-detection.outputs.languages }} + PR_FILES: ${{ steps.pr-files.outputs.files }} + - name: Check if report is generated run: | if [ -s ${GITHUB_WORKSPACE}/${WORKDIR}/final.out ]; then @@ -69,21 +102,48 @@ runs: exit 1 fi shell: bash + env: + WORKDIR: ${{ inputs.working-directory }} + - name: Comment on PR if: success() uses: actions/github-script@v6 env: - KEPLOY_REPORT: ${{ steps.keploy-test-report.outputs.KEPLOY_REPORT }} + KEPLOY_REPORT: ${{ steps.parse-results.outputs.KEPLOY_REPORT }} with: - github-token: ${{ github.token }} + github-token: ${{ inputs.github-token }} script: | if (!process.env.KEPLOY_REPORT) { console.error('Error: KEPLOY_REPORT not found.'); process.exit(1); } + + const fs = require('fs'); + const path = require('path'); + + function readReport(filepath) { + try { + return fs.readFileSync(filepath, 'utf8'); + } catch (error) { + return null; + } + } + + const reportsDir = path.join('${{ inputs.working-directory }}', 'analysis-reports'); + const reports = fs.readdirSync(reportsDir) + .filter(file => file.endsWith('.txt') || file.endsWith('.xml')) + .map(file => readReport(path.join(reportsDir, file))) + .filter(Boolean); + + const summary = `## 📊 TestGPT Analysis Report + + ${process.env.KEPLOY_REPORT || 'No test results available'} + + ${reports.join('\n\n')}`; + github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - body: process.env.KEPLOY_REPORT - }) + body: summary + }) \ No newline at end of file diff --git a/analyze-code.sh b/analyze-code.sh new file mode 100644 index 0000000..7c981f1 --- /dev/null +++ b/analyze-code.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +# Change to the workspace directory +cd $GITHUB_WORKSPACE/$WORKDIR + +# Ensure analysis-reports directory exists +mkdir -p analysis-reports + +# Function to find existing linter installations +find_linter() { + local linter_name=$1 + local system_path=$(which $linter_name 2>/dev/null) + echo $system_path +} + +# Log message function +log_message() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" +} + +log_message "Starting code analysis" + +for lang in $DETECTED_LANGUAGES; do + case "$lang" in + go) + GO_LINTER=$(find_linter golangci-lint) + if [ -z "$GO_LINTER" ]; then + log_message "Installing golangci-lint..." + go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + else + log_message "Using system golangci-lint: $GO_LINTER" + fi + ;; + javascript) + JS_LINTER=$(find_linter eslint) + if [ -z "$JS_LINTER" ]; then + log_message "Installing eslint..." + npm install -g eslint + else + log_message "Using system eslint: $JS_LINTER" + fi + ;; + python) + PY_LINTER=$(find_linter flake8) + PY_SECURITY=$(find_linter bandit) + if [ -z "$PY_LINTER" ]; then + log_message "Installing flake8..." + pip install flake8 + else + log_message "Using system flake8: $PY_LINTER" + fi + if [ -z "$PY_SECURITY" ]; then + log_message "Installing bandit..." + pip install bandit + else + log_message "Using system bandit: $PY_SECURITY" + fi + ;; + esac +done + +if [ -n "$PR_FILES" ]; then + IFS=',' read -ra FILES <<< "$PR_FILES" + + for lang in $DETECTED_LANGUAGES; do + case "$lang" in + go) + GO_FILES=() + for file in "${FILES[@]}"; do + if [[ $file == *.go ]]; then + GO_FILES+=("$file") + fi + done + if [ ${#GO_FILES[@]} -gt 0 ]; then + golangci-lint run --out-format=github-actions "${GO_FILES[@]}" > analysis-reports/go-lint.txt 2>&1 || true + else + echo "No Go files changed in PR" > analysis-reports/go-lint.txt + fi + ;; + javascript) + JS_FILES=() + for file in "${FILES[@]}"; do + if [[ $file == *.js || $file == *.ts || $file == *.jsx || $file == *.tsx ]]; then + JS_FILES+=("$file") + fi + done + if [ ${#JS_FILES[@]} -gt 0 ] && ([ -f ".eslintrc.js" ] || [ -f ".eslintrc.json" ] || [ -f ".eslintrc.yml" ]); then + eslint "${JS_FILES[@]}" --format junit -o analysis-reports/js-lint.xml 2>&1 || true + else + echo "No JavaScript/TypeScript files changed in PR or no ESLint config found" > analysis-reports/js-lint.txt + fi + ;; + python) + PY_FILES=() + for file in "${FILES[@]}"; do + if [[ $file == *.py ]]; then + PY_FILES+=("$file") + fi + done + if [ ${#PY_FILES[@]} -gt 0 ]; then + flake8 "${PY_FILES[@]}" --output-file=analysis-reports/py-lint.txt 2>&1 || true + bandit -r "${PY_FILES[@]}" -f txt -o analysis-reports/py-security.txt 2>&1 || true + else + echo "No Python files changed in PR" > analysis-reports/py-lint.txt + fi + ;; + esac + done +else + for lang in $DETECTED_LANGUAGES; do + case "$lang" in + go) + golangci-lint run --out-format=github-actions ./... > analysis-reports/go-lint.txt 2>&1 || true + ;; + javascript) + if [ -f ".eslintrc.js" ] || [ -f ".eslintrc.json" ] || [ -f ".eslintrc.yml" ]; then + eslint . --format junit -o analysis-reports/js-lint.xml 2>&1 || true + else + echo "No ESLint config found, skipping JavaScript linting" > analysis-reports/js-lint.txt + fi + ;; + python) + flake8 . --output-file=analysis-reports/py-lint.txt 2>&1 || true + bandit -r . -f txt -o analysis-reports/py-security.txt 2>&1 || true + ;; + esac + done +fi + +temp_file=$(mktemp) +for report in analysis-reports/*; do + if [ -f "$report" ]; then + echo "### $(basename "$report")" >> "$temp_file" + cat "$report" >> "$temp_file" + echo -e "\n---\n" >> "$temp_file" + fi +done +mv "$temp_file" analysis-reports/final-report.txt + +log_message "Code analysis completed" \ No newline at end of file diff --git a/detect-languages.sh b/detect-languages.sh new file mode 100644 index 0000000..f2d2750 --- /dev/null +++ b/detect-languages.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +declare -A extensions=( + ["go"]="go" + ["js"]="javascript" + ["ts"]="javascript" + ["jsx"]="javascript" + ["tsx"]="javascript" + ["py"]="python" + ["java"]="java" +) + +LANGUAGES="" + +# Exclude common non-source files +while IFS= read -r file; do + ext="${file##*.}" + if [[ -n "${extensions[$ext]}" ]]; then + LANGUAGES+="${extensions[$ext]} " + fi +done < <(find . -type f \ + -not -path "*/\.*/*" \ + -not -path "*/node_modules/*" \ + -not -path "*/dist/*" \ + -not -path "*/build/*" \ + -not -path "*/target/*" \ + -not -path "*/vendor/*" \ + -not -path "*/__pycache__/*" \ + -size -1M) + +UNIQUE_LANGS=$(echo "$LANGUAGES" | tr ' ' '\n' | sort -u | tr '\n' ' ') +echo "languages=$UNIQUE_LANGS" >> $GITHUB_OUTPUT \ No newline at end of file diff --git a/parse-results.sh b/parse-results.sh new file mode 100644 index 0000000..2072365 --- /dev/null +++ b/parse-results.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +mkdir -p "${GITHUB_WORKSPACE}/${WORKDIR}" + +ANSI_FILTER="s/\x1B\[([0-9]{1,3}(;[0-9]{1,3})*)?[mGK]//g" + +if [ ! -f "${GITHUB_WORKSPACE}/${WORKDIR}/report.txt" ]; then + echo "ERROR: Test report file not found. Please ensure tests were executed correctly." > "${GITHUB_WORKSPACE}/${WORKDIR}/final.out" +else + grep -oE "COMPLETE TESTRUN SUMMARY\.\s+Total tests: [0-9]+" "${GITHUB_WORKSPACE}/${WORKDIR}/report.txt" | sed -r "${ANSI_FILTER}" > "${GITHUB_WORKSPACE}/${WORKDIR}/final_total_tests.out" + grep -oE "COMPLETE TESTRUN SUMMARY\.\s+Total test passed: [0-9]+" "${GITHUB_WORKSPACE}/${WORKDIR}/report.txt" | sed -r "${ANSI_FILTER}" > "${GITHUB_WORKSPACE}/${WORKDIR}/final_total_passed.out" + grep -oE "COMPLETE TESTRUN SUMMARY\.\s+Total test failed: [0-9]+" "${GITHUB_WORKSPACE}/${WORKDIR}/report.txt" | sed -r "${ANSI_FILTER}" > "${GITHUB_WORKSPACE}/${WORKDIR}/final_total_failed.out" + + if [ ! -s "${GITHUB_WORKSPACE}/${WORKDIR}/final_total_tests.out" ]; then + echo "ERROR: No valid test summary found in report file. Please check test execution output format." > "${GITHUB_WORKSPACE}/${WORKDIR}/final.out" + else + cat "${GITHUB_WORKSPACE}/${WORKDIR}/final_total_tests.out" "${GITHUB_WORKSPACE}/${WORKDIR}/final_total_passed.out" "${GITHUB_WORKSPACE}/${WORKDIR}/final_total_failed.out" > "${GITHUB_WORKSPACE}/${WORKDIR}/final.out" + fi +fi + +echo 'KEPLOY_REPORT<> $GITHUB_OUTPUT +cat "${GITHUB_WORKSPACE}/${WORKDIR}/final.out" >> $GITHUB_OUTPUT +echo 'EOF' >> $GITHUB_OUTPUT \ No newline at end of file