diff --git a/.github/workflows/dev-to-main-pr.yml b/.github/workflows/dev-to-main-pr.yml new file mode 100644 index 0000000..587b2b4 --- /dev/null +++ b/.github/workflows/dev-to-main-pr.yml @@ -0,0 +1,330 @@ +name: 🚀 Dev Branch CI/CD Pipeline + +on: + push: + branches: [ dev ] + workflow_dispatch: + +env: + NODE_VERSION: '18' + +jobs: + code-quality-check: + name: 🔍 Code Quality & Syntax Check + runs-on: ubuntu-latest + + outputs: + checks-passed: ${{ steps.quality-check.outputs.passed }} + check-results: ${{ steps.quality-check.outputs.results }} + + steps: + - name: 📥 Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: 📦 Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: 📥 Install dependencies + run: npm ci + + - name: 🏗️ Generate Prisma Client + run: npx prisma generate + + - name: 🔍 TypeScript Compilation Check + id: typescript-check + run: | + echo "🔍 Running TypeScript compilation check..." + if npm run type-check; then + echo "✅ TypeScript compilation successful" + echo "typescript=passed" >> $GITHUB_OUTPUT + else + echo "❌ TypeScript compilation failed" + echo "typescript=failed" >> $GITHUB_OUTPUT + exit 1 + fi + + - name: 🏗️ Build Check + id: build-check + run: | + echo "🏗️ Running build check..." + if npm run build; then + echo "✅ Build successful" + echo "build=passed" >> $GITHUB_OUTPUT + else + echo "❌ Build failed" + echo "build=failed" >> $GITHUB_OUTPUT + exit 1 + fi + + - name: 📋 Code Quality Summary + id: quality-check + run: | + echo "📋 Generating code quality summary..." + + # Get commit info + COMMIT_SHA="${{ github.sha }}" + COMMIT_MSG=$(git log -1 --pretty=format:"%s" $COMMIT_SHA) + COMMIT_AUTHOR=$(git log -1 --pretty=format:"%an" $COMMIT_SHA) + + # Get changed files + CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD | tr '\n' ',' | sed 's/,$//') + + # Create summary + SUMMARY="✅ All code quality checks passed! + + 📊 **Check Results:** + - ✅ TypeScript Compilation: Passed + - ✅ Build Process: Passed + + 📝 **Commit Info:** + - SHA: \`${COMMIT_SHA:0:7}\` + - Message: ${COMMIT_MSG} + - Author: ${COMMIT_AUTHOR} + + 📁 **Changed Files:** ${CHANGED_FILES}" + + echo "passed=true" >> $GITHUB_OUTPUT + echo "results<> $GITHUB_OUTPUT + echo "$SUMMARY" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + create-pull-request: + name: 🤖 Create AI-Generated Pull Request + runs-on: ubuntu-latest + needs: code-quality-check + if: needs.code-quality-check.outputs.checks-passed == 'true' + + steps: + - name: 📥 Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: 🤖 Generate PR Content with AI + id: generate-pr + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + run: | + echo "🤖 Generating AI-powered PR content..." + + # Get commit info and diff + COMMIT_SHA="${{ github.sha }}" + COMMIT_MSG=$(git log -1 --pretty=format:"%s" $COMMIT_SHA) + COMMIT_AUTHOR=$(git log -1 --pretty=format:"%an" $COMMIT_SHA) + COMMIT_DATE=$(git log -1 --pretty=format:"%ci" $COMMIT_SHA) + + # Get detailed diff (limited to avoid too large payload) + GIT_DIFF=$(git diff HEAD~1 HEAD --unified=3 | head -c 3000) + + # Get changed files with stats + CHANGED_FILES=$(git diff --stat HEAD~1 HEAD) + + # Check if OpenAI API key is available + if [ -z "$OPENAI_API_KEY" ]; then + echo "⚠️ OpenAI API key not found, using fallback PR content" + PR_TITLE="🚀 Dev → Main: ${COMMIT_MSG}" + PR_BODY="**Summary:** ${COMMIT_MSG} + + **Code Quality Status:** + ${{ needs.code-quality-check.outputs.check-results }} + + **Changed Files:** + ${CHANGED_FILES} + + **Auto-generated PR from dev branch** 🤖 + + > Note: Add OPENAI_API_KEY secret for AI-generated descriptions" + else + echo "🔑 OpenAI API key found, generating AI content..." + + # Create AI prompt + AI_PROMPT="You are a senior software engineer reviewing code changes for a Node.js/TypeScript candidate matching API. + + Generate a professional pull request description based on this information: + + **Commit Information:** + - Message: ${COMMIT_MSG} + - Author: ${COMMIT_AUTHOR} + - Date: ${COMMIT_DATE} + - SHA: ${COMMIT_SHA:0:7} + + **Files Changed:** + ${CHANGED_FILES} + + **Code Diff (truncated):** + ${GIT_DIFF} + + Please generate: + 1. A clear, professional PR title + 2. A comprehensive PR description with: + - Summary of changes + - Technical details + - Testing status + - Any breaking changes + - Code quality improvements + + Format the response as JSON with 'title' and 'body' fields. Make it professional and detailed." + + # Call OpenAI API with error handling + PR_CONTENT=$(curl -s -w "%{http_code}" -X POST "https://api.openai.com/v1/chat/completions" \ + -H "Authorization: Bearer $OPENAI_API_KEY" \ + -H "Content-Type: application/json" \ + -d "{ + \"model\": \"gpt-4\", + \"messages\": [ + { + \"role\": \"system\", + \"content\": \"You are a senior software engineer who writes excellent pull request descriptions. Always respond with valid JSON containing 'title' and 'body' fields.\" + }, + { + \"role\": \"user\", + \"content\": $(echo "$AI_PROMPT" | jq -R -s .) + } + ], + \"max_tokens\": 1000, + \"temperature\": 0.3 + }") + + # Extract HTTP status code + HTTP_STATUS=$(echo "$PR_CONTENT" | tail -c 4) + PR_RESPONSE=$(echo "$PR_CONTENT" | head -c -4) + + echo "📝 OpenAI API Response Status: $HTTP_STATUS" + + if [ "$HTTP_STATUS" = "200" ]; then + echo "✅ AI Response received successfully" + # Extract title and body from AI response + AI_CONTENT=$(echo "$PR_RESPONSE" | jq -r '.choices[0].message.content' 2>/dev/null) + PR_TITLE=$(echo "$AI_CONTENT" | jq -r '.title' 2>/dev/null || echo "🚀 Dev → Main: ${COMMIT_MSG}") + PR_BODY=$(echo "$AI_CONTENT" | jq -r '.body' 2>/dev/null || echo "**Summary:** ${COMMIT_MSG} + + **Code Quality Status:** + ${{ needs.code-quality-check.outputs.check-results }} + + **Auto-generated PR from dev branch** 🤖") + else + echo "❌ OpenAI API call failed with status $HTTP_STATUS" + echo "Response: $PR_RESPONSE" + # Fallback content + PR_TITLE="🚀 Dev → Main: ${COMMIT_MSG}" + PR_BODY="**Summary:** ${COMMIT_MSG} + + **Code Quality Status:** + ${{ needs.code-quality-check.outputs.check-results }} + + **Changed Files:** + ${CHANGED_FILES} + + **Auto-generated PR from dev branch** 🤖 + + > Note: AI generation failed, using fallback content" + fi + fi + + # Set outputs + echo "title<> $GITHUB_OUTPUT + echo "$PR_TITLE" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "body<> $GITHUB_OUTPUT + echo "$PR_BODY" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: 🔄 Create Pull Request + id: create-pr + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "🔄 Creating pull request from dev to main..." + + # Check if PR already exists + EXISTING_PR=$(gh pr list --base main --head dev --json number --jq '.[0].number' 2>/dev/null || echo "") + + if [ -n "$EXISTING_PR" ]; then + echo "📝 Updating existing PR #$EXISTING_PR" + gh pr edit $EXISTING_PR \ + --title "${{ steps.generate-pr.outputs.title }}" \ + --body "${{ steps.generate-pr.outputs.body }} + + --- + + 🤖 **Auto-generated Pull Request** + - 📊 **Pipeline Run:** #${{ github.run_number }} + - 🔗 **Workflow:** [${{ github.workflow }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + - 📅 **Updated:** $(date -u) + - 👤 **Triggered by:** ${{ github.event.head_commit.author.name }} + + **Code Quality Checks:** + ${{ needs.code-quality-check.outputs.check-results }}" + + echo "pull-request-number=$EXISTING_PR" >> $GITHUB_OUTPUT + echo "pull-request-url=$(gh pr view $EXISTING_PR --json url --jq '.url')" >> $GITHUB_OUTPUT + else + echo "🆕 Creating new pull request" + PR_URL=$(gh pr create \ + --base main \ + --head dev \ + --title "${{ steps.generate-pr.outputs.title }}" \ + --body "${{ steps.generate-pr.outputs.body }} + + --- + + 🤖 **Auto-generated Pull Request** + - 📊 **Pipeline Run:** #${{ github.run_number }} + - 🔗 **Workflow:** [${{ github.workflow }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + - 📅 **Created:** $(date -u) + - 👤 **Triggered by:** ${{ github.event.head_commit.author.name }} + + **Code Quality Checks:** + ${{ needs.code-quality-check.outputs.check-results }}") + + PR_NUMBER=$(echo "$PR_URL" | sed 's/.*\/pull\///') + echo "pull-request-number=$PR_NUMBER" >> $GITHUB_OUTPUT + echo "pull-request-url=$PR_URL" >> $GITHUB_OUTPUT + fi + + - name: 👥 Assign Reviewers + if: steps.create-pr.outputs.pull-request-number + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "👥 Assigning reviewers to PR #${{ steps.create-pr.outputs.pull-request-number }}" + + # Try to assign github-actions bot as reviewer (if available) + # Note: You may need to replace this with actual usernames that have access to your repo + gh pr edit ${{ steps.create-pr.outputs.pull-request-number }} \ + --add-reviewer firatyll \ + --add-label "auto-generated,dev-to-main,code-review-needed" || echo "Could not assign reviewers" + + - name: 📝 Comment on PR + if: steps.create-pr.outputs.pull-request-number + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh pr comment ${{ steps.create-pr.outputs.pull-request-number }} --body "🤖 **Automated PR Creation Complete!** + + ✅ **Code Quality Checks Passed:** + - TypeScript compilation + - Build process + - Syntax validation + + 🔍 **Review Checklist:** + - [ ] Code changes are logical and well-structured + - [ ] No breaking changes introduced + - [ ] API endpoints function correctly + - [ ] Database schema changes are safe + - [ ] Error handling is appropriate + + 🚀 **Ready for review!** @firatyll" + + - name: 📢 Summary + run: | + echo "🎉 Workflow completed successfully!" + echo "📝 Pull Request: ${{ steps.create-pr.outputs.pull-request-url }}" + echo "🔢 PR Number: ${{ steps.create-pr.outputs.pull-request-number }}" diff --git a/README.md b/README.md index fe8797e..3e67a8f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,21 @@ # Candidate Matching API +[![Dev to Main PR](https://github.com/firatyll/candidate-matching/actions/workflows/dev-to-main-pr.yml/badge.svg)](https://github.com/firatyll/candidate-matching/actions/workflows/dev-to-main-pr.yml) + > **Note: This application is currently under active development. Features and API endpoints may change.** A RESTful API service for candidate and job position matching built with Node.js, Express, TypeScript, Prisma, and PostgreSQL. +## 🚀 CI/CD Pipeline + +This repository includes an automated GitHub Actions pipeline that: +- ✅ Runs code quality checks on every push to `dev` branch +- 🤖 Generates AI-powered pull requests using OpenAI GPT-4 +- 📋 Automatically assigns reviewers and adds helpful labels +- 🔍 Includes comprehensive review checklists + +See [Pipeline Setup Guide](./.github/PIPELINE_SETUP.md) for configuration details. + ## Features - **Candidate Management**: Create, read, update, and delete candidate profiles diff --git a/package.json b/package.json index 679080c..f1ae3ff 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,9 @@ "prestart": "npm run build", "serve": "npm run build && npm start", "type-check": "tsc --noEmit", - "lint": "echo 'No linter configured yet'", + "lint": "tsc --noEmit --strict && echo '✅ TypeScript strict mode check passed'", + "lint:check": "npm run type-check", + "validate": "npm run lint && npm run build", "test": "echo 'No tests configured yet'" }, "keywords": [],