From 751a2811188056cdf39c8376458aabf66b322e1a Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Sun, 30 Nov 2025 18:39:52 +0530 Subject: [PATCH 01/21] feat: adding project in workflow --- .github/workflows/add-pr-to-devops.yml | 22 ++++++++++++++++++++++ .github/workflows/auto-reviewer.yml | 7 ++++--- .github/workflows/bot-handler.yml | 3 ++- 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/add-pr-to-devops.yml diff --git a/.github/workflows/add-pr-to-devops.yml b/.github/workflows/add-pr-to-devops.yml new file mode 100644 index 0000000..7971603 --- /dev/null +++ b/.github/workflows/add-pr-to-devops.yml @@ -0,0 +1,22 @@ +name: Add PR to DevOps Board + +on: + pull_request: + types: [opened, reopened] + branches: [master] + push: + branches: [master] + +jobs: + add_to_project: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Add PR to DevOps Release Board + uses: actions/add-to-project@v0.5.0 + with: + project-url: https://github.com/orgs/dhwani-ris/projects/## + github-token: ${{ secrets.GITHUB_TOKEN }} + diff --git a/.github/workflows/auto-reviewer.yml b/.github/workflows/auto-reviewer.yml index 5ba1e01..f97e436 100644 --- a/.github/workflows/auto-reviewer.yml +++ b/.github/workflows/auto-reviewer.yml @@ -2,7 +2,8 @@ name: Auto Request Review on: pull_request: - types: [opened, synchronize, reopened, ready_for_review] + types: [opened, synchronize, reopened, ready_for_review, closed] + branches: [master] permissions: pull-requests: write @@ -13,8 +14,8 @@ jobs: name: Request Review from Default Reviewer runs-on: ubuntu-latest if: | - github.event.pull_request.base.ref == 'main' || - github.event.pull_request.base.ref == 'master' + (github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'synchronize' || github.event.action == 'ready_for_review') && + (github.event.pull_request.base.ref == 'main' || github.event.pull_request.base.ref == 'master') steps: - name: Request review from default reviewer diff --git a/.github/workflows/bot-handler.yml b/.github/workflows/bot-handler.yml index 8b0476a..050c0ed 100644 --- a/.github/workflows/bot-handler.yml +++ b/.github/workflows/bot-handler.yml @@ -4,7 +4,8 @@ on: issue_comment: types: [created, edited] pull_request: - types: [opened, synchronize, reopened] + types: [opened, synchronize, reopened, closed] + branches: [master] permissions: contents: write From b02880aa958f81bc6895bebe1618567343da9e05 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Sun, 30 Nov 2025 18:51:54 +0530 Subject: [PATCH 02/21] fix: updated the deployment note and devops checklist --- .github/workflows/add-pr-to-devops.yml | 9 +- .github/workflows/devops-checklist.yml | 132 +++++-------------------- 2 files changed, 30 insertions(+), 111 deletions(-) diff --git a/.github/workflows/add-pr-to-devops.yml b/.github/workflows/add-pr-to-devops.yml index 7971603..08b6733 100644 --- a/.github/workflows/add-pr-to-devops.yml +++ b/.github/workflows/add-pr-to-devops.yml @@ -3,16 +3,19 @@ name: Add PR to DevOps Board on: pull_request: types: [opened, reopened] - branches: [master] - push: - branches: [master] + branches: [main, master] jobs: add_to_project: runs-on: ubuntu-latest + if: | + github.event.pull_request.base.ref == 'main' || + github.event.pull_request.base.ref == 'master' permissions: contents: read pull-requests: write + repository-projects: write + organization-projects: write steps: - name: Add PR to DevOps Release Board uses: actions/add-to-project@v0.5.0 diff --git a/.github/workflows/devops-checklist.yml b/.github/workflows/devops-checklist.yml index 1bc8956..6efce84 100644 --- a/.github/workflows/devops-checklist.yml +++ b/.github/workflows/devops-checklist.yml @@ -181,87 +181,6 @@ jobs: |-------|-----------------|----------------|-----------------| | 1. | \`${context.repo.repo}\` | \`${pr.base.ref}-release-${version}\` | ${formatFeatureDetails(featureDetails)} | - **Dependencies:** - - Dependencies updated: \`TBD\` *(Please review and update)* - \`\`\` - - \`\`\` - - **Database Changes (Queries to run):** - - Database changes required: \`TBD\` *(Please review and update)* - \`\`\` - - \`\`\` - - **Testing:** - - [ ] Unit tests passed - - [ ] Integration tests passed - - [ ] E2E tests passed - - [ ] Manual testing completed - \`\`\` - - \`\`\` - - **Known Issues:** - - Known issues: \`TBD\` *(Please review and update)* - \`\`\` - - \`\`\` - - **Contact Information:** - - Support Team Email: \`\`\`\`\`\` - - Support Team Phone: \`\`\`\`\`\` - - **Attachments:** - - Deployment files attached/committed: \`TBD\` *(Please review and update)* - \`\`\` - - \`\`\` - - --- - - ### For DevOps Team Use Only - *(To be filled by the DevOps team after deploying the release)* - - **Deployment Details:** - - Date and time of deployment: \`\`\`\`\`\` - - Deployed by: \`\`\`\`\`\` - - Deployment Status: \`\`\`\`\`\` - - **Deployment Instructions:** - - [ ] Pre-deployment tasks completed (backups, etc.) - - [ ] Production environment accessed securely - - [ ] Latest release pulled from version control - - [ ] Dependencies installed/updated - - [ ] Database migrations run (if applicable) - - [ ] Application services restarted - - [ ] Deployment monitored and verified - - **Rollback Plan:** - - [ ] Rollback procedure documented - - [ ] Previous version tag identified: \`\`\`\`\`\` - - [ ] Database rollback scripts prepared (if applicable) - - [ ] Rollback tested in staging environment - - **Post-Deployment Checklist:** - - [ ] Service availability and response times verified - - [ ] System resources monitored - - [ ] Critical user scenarios tested - - [ ] Data integrity confirmed - - [ ] Error logs reviewed - - [ ] Security scans completed - - [ ] Server and infrastructure health checked - - [ ] Backup and disaster recovery procedures validated - - **Notes:** - \`\`\` - - \`\`\` - - **Acknowledgment:** - - [ ] Deployment acknowledged and system ready for production use - - --- **Note:** This deployment document was **automatically generated** from PR commits and information. Please review and update the TBD sections before merging.`; // Check if comment already exists @@ -320,6 +239,26 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const pr = context.payload.pull_request; + + // Check if comment already exists + const comments = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + }); + + const existingComment = comments.data.find( + comment => comment.user.type === 'Bot' && + comment.body.includes('DevOps Checklist - Workflow Review') + ); + + if (existingComment) { + // Don't update existing comment to preserve checkbox states + console.log('DevOps Checklist comment already exists, preserving user checkboxes'); + return; + } + + // Only create new comment if it doesn't exist const checklist = `## ๐Ÿ”ง DevOps Checklist - Workflow Review **Please review all workflows and checks before merging:** @@ -350,35 +289,12 @@ jobs: --- **Note:** This checklist is for DevOps team to verify all workflows and checks before merging.`; - // Check if comment already exists - const comments = await github.rest.issues.listComments({ + // Create new comment + await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: pr.number, + body: checklist }); - - const existingComment = comments.data.find( - comment => comment.user.type === 'Bot' && - comment.body.includes('DevOps Checklist - Workflow Review') - ); - - if (existingComment) { - // Update existing comment - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existingComment.id, - body: checklist - }); - console.log('Updated existing DevOps Checklist comment'); - } else { - // Create new comment - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - body: checklist - }); - console.log('Created new DevOps Checklist comment'); - } + console.log('Created new DevOps Checklist comment'); From 66d8889a1d3c29cd62b25fad7f22f576862ec826 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Sun, 30 Nov 2025 19:34:16 +0530 Subject: [PATCH 03/21] fix: updated the devops checklist and correct the checklist submission --- .github/workflows/devops-checklist-submit.yml | 124 ++++++++++++++++++ .github/workflows/devops-checklist.yml | 96 ++++++++++---- 2 files changed, 197 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/devops-checklist-submit.yml diff --git a/.github/workflows/devops-checklist-submit.yml b/.github/workflows/devops-checklist-submit.yml new file mode 100644 index 0000000..33d700d --- /dev/null +++ b/.github/workflows/devops-checklist-submit.yml @@ -0,0 +1,124 @@ +name: DevOps Checklist Submission + +on: + issue_comment: + types: [created] + +permissions: + pull-requests: write + contents: read + +jobs: + submit-checklist: + runs-on: ubuntu-latest + if: | + github.event.issue.pull_request && + contains(github.event.comment.body, '/submit-checklist') + steps: + - name: Get PR number + id: get-pr + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = context.payload.issue.number; + core.setOutput('pr_number', prNumber); + return prNumber; + + - name: Submit and lock checklist + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = context.payload.issue.number; + const submitter = context.payload.comment.user.login; + const submitTime = new Date().toISOString(); + const submitDate = new Date().toLocaleString('en-US', { + timeZone: 'UTC', + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + timeZoneName: 'short' + }); + + // Get all comments to find the checklist + const comments = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + }); + + const checklistComment = comments.data.find( + comment => comment.user.type === 'Bot' && + comment.body.includes('DevOps Checklist - Workflow Review') + ); + + if (!checklistComment) { + console.log('Checklist comment not found'); + return; + } + + // Check if already submitted + if (checklistComment.body.includes('โœ… **CHECKLIST SUBMITTED**')) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: `โš ๏ธ This checklist has already been submitted and cannot be modified.` + }); + return; + } + + // Extract the current checklist content (preserve checkboxes) + let checklistBody = checklistComment.body; + + // Replace the submission section with submitted status + const submittedSection = `--- + + ### โœ… **CHECKLIST SUBMITTED** + + **Submitted by:** @${submitter} + **Submitted at:** ${submitDate} (UTC) + + ๐Ÿ”’ **This checklist is now locked and cannot be modified.** + + --- + **Note:** This checklist was submitted and is final. No further changes can be made.`; + + // Find and replace the submission section + // Look for the "Submit Checklist" section and replace everything from there to the end + const submitSectionStart = checklistBody.indexOf('### ๐Ÿ“ค Submit Checklist'); + if (submitSectionStart !== -1) { + // Keep everything before the submission section, then add the submitted section + checklistBody = checklistBody.substring(0, submitSectionStart) + submittedSection; + } else { + // If section not found, append the submitted section + checklistBody = checklistBody + '\n\n' + submittedSection; + } + + // Update the checklist comment to show it's submitted + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: checklistComment.id, + body: checklistBody + }); + + // Add a confirmation comment + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: `โœ… **Checklist submitted successfully!** + + The DevOps Checklist has been locked and cannot be modified. + + **Submitted by:** @${submitter} + **Time:** ${submitDate} (UTC)` + }); + + console.log(`Checklist submitted by ${submitter} at ${submitTime}`); + diff --git a/.github/workflows/devops-checklist.yml b/.github/workflows/devops-checklist.yml index 6efce84..aa6eb8e 100644 --- a/.github/workflows/devops-checklist.yml +++ b/.github/workflows/devops-checklist.yml @@ -253,6 +253,11 @@ jobs: ); if (existingComment) { + // Check if already submitted + if (existingComment.body.includes('โœ… **CHECKLIST SUBMITTED**')) { + console.log('Checklist already submitted and locked, cannot update'); + return; + } // Don't update existing comment to preserve checkbox states console.log('DevOps Checklist comment already exists, preserving user checkboxes'); return; @@ -263,31 +268,76 @@ jobs: **Please review all workflows and checks before merging:** - ### Workflow Status Review - - [ ] All CI/CD workflows are passing - - [ ] Quality Checks workflow passed - - [ ] Security Scan workflow passed - - [ ] Code quality checks passed - - [ ] Test coverage meets requirements - - ### Review Status - - [ ] All required reviewers have approved - - [ ] Code review completed - - [ ] Security review completed (if applicable) - - ### Pre-Merge Verification - - [ ] Deployment Notes document reviewed (see Deployment Notes comment above) - - [ ] All commits reviewed - - [ ] Breaking changes identified (if any) - - [ ] Version number verified (if applicable) - - ### Final Checks - - [ ] No blocking issues or errors - - [ ] Ready for production deployment - - [ ] Rollback plan understood (if high-risk) + ### 1. Workflow Status Review + - [ ] DevOps Checklist Reminder + - [ ] Documentation Required + - [ ] Semantic Commits + - [ ] GitHub Actions Security (send-secrets) + - [ ] Quality Checks / Pre-Commit + - [ ] Quality Checks / Semgrep Rules + - [ ] All other workflows passing + + ### 2. Code Review & Approval + - [ ] Team Lead has approved the PR + - [ ] All reviewer comments resolved + - [ ] No leftover debug logs, temp files, zip files, or accidental commits + - [ ] No exposed secrets or credentials + - [ ] Commit messages follow semantic standards + + ### 3. Documentation & Release Notes + - [ ] Deployment Notes reviewed + - [ ] Change summary provided + - [ ] Impacted modules/services listed + - [ ] Config or environment changes documented + - [ ] Rollback Plan attached + - [ ] ๐Ÿ”— Rollback Reference: https://devops.dhwaniris.com/rollback-guidelines + + ### 4. Functional & QA Validation + - [ ] Changes tested on staging/UAT + - [ ] QA sign-off received + - [ ] No high/critical bugs pending + - [ ] Regression testing completed + - [ ] API responses verified (if applicable) + + ### 5. Security & Compliance + - [ ] Semgrep Rules passed (mandatory) + - [ ] No new security warnings introduced + - [ ] Sensitive logic reviewed + - [ ] No secrets in PR (manual confirmation) + + ### 6. Deployment Verification + - [ ] Deployment strategy identified (Blue/Green / Rolling / Manual) + - [ ] Downtime estimate validated (if applicable) + - [ ] Backup/restore plan ready + - [ ] Monitoring alerts configured or validated + - [ ] Pre-deployment checks completed (DB migrations, queues, cron, cache, etc.) + + ### 7. Merge Validation + - [ ] PR raised from development โ†’ master (correct flow) + - [ ] Branch updated with latest master (no conflicts) + - [ ] All merge blockers cleared + - [ ] No pending tasks or approvals + + ### 8. Final DevOps Decision + - [ ] **GO** โ†’ Safe to merge into production + - [ ] **NO-GO** โ†’ Blocked; failures listed below: + + **Notes:** + \`\`\` + + \`\`\` --- - **Note:** This checklist is for DevOps team to verify all workflows and checks before merging.`; + + ### ๐Ÿ“ค Submit Checklist + + **To submit this checklist (cannot be reverted once submitted):** + + Comment: \`/submit-checklist\` + + โš ๏ธ **Warning:** Once submitted, this checklist will be locked and cannot be modified. + + **Status:** โณ *Pending Submission*`; // Create new comment await github.rest.issues.createComment({ From 6b516111be353bc27e591f93a3cad7fb362fff56 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Sun, 30 Nov 2025 19:41:42 +0530 Subject: [PATCH 04/21] fix: correct the checklist submission --- .github/workflows/devops-checklist-submit.yml | 126 +++++++++++++++++- .github/workflows/devops-checklist.yml | 5 +- 2 files changed, 124 insertions(+), 7 deletions(-) diff --git a/.github/workflows/devops-checklist-submit.yml b/.github/workflows/devops-checklist-submit.yml index 33d700d..89a6820 100644 --- a/.github/workflows/devops-checklist-submit.yml +++ b/.github/workflows/devops-checklist-submit.yml @@ -2,7 +2,9 @@ name: DevOps Checklist Submission on: issue_comment: - types: [created] + types: [created, edited] + pull_request: + types: [synchronize, reopened, ready_for_review, closed] permissions: pull-requests: write @@ -12,8 +14,8 @@ jobs: submit-checklist: runs-on: ubuntu-latest if: | - github.event.issue.pull_request && - contains(github.event.comment.body, '/submit-checklist') + (github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '/submit-checklist')) || + (github.event_name == 'pull_request' && (github.event.action == 'synchronize' || github.event.action == 'reopened' || github.event.action == 'ready_for_review' || github.event.action == 'closed')) steps: - name: Get PR number id: get-pr @@ -26,6 +28,7 @@ jobs: return prNumber; - name: Submit and lock checklist + if: github.event_name == 'issue_comment' && contains(github.event.comment.body, '/submit-checklist') uses: actions/github-script@v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -75,16 +78,21 @@ jobs: // Extract the current checklist content (preserve checkboxes) let checklistBody = checklistComment.body; + // Extract submission info to preserve it + const submissionInfo = `**Submitted by:** @${submitter} + **Submitted at:** ${submitDate} (UTC)`; + // Replace the submission section with submitted status const submittedSection = `--- ### โœ… **CHECKLIST SUBMITTED** - **Submitted by:** @${submitter} - **Submitted at:** ${submitDate} (UTC) + ${submissionInfo} ๐Ÿ”’ **This checklist is now locked and cannot be modified.** + + --- **Note:** This checklist was submitted and is final. No further changes can be made.`; @@ -121,4 +129,112 @@ jobs: }); console.log(`Checklist submitted by ${submitter} at ${submitTime}`); + + - name: Re-lock checklist if edited after submission + if: | + (github.event_name == 'issue_comment' && github.event.action == 'edited') || + (github.event_name == 'pull_request') + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = github.event_name == 'issue_comment' + ? context.payload.issue.number + : context.payload.pull_request.number; + + // Get all comments to find the checklist + const comments = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + }); + + const checklistComment = comments.data.find( + comment => comment.user.type === 'Bot' && + comment.body.includes('DevOps Checklist - Workflow Review') + ); + + if (!checklistComment) { + return; + } + + // Check if checklist was previously submitted (look for submission marker) + const hasSubmissionMarker = checklistComment.body.includes('CHECKLIST_SUBMITTED_LOCK'); + const hasSubmittedText = checklistComment.body.includes('โœ… **CHECKLIST SUBMITTED**'); + + // If it was submitted, ensure it's locked + if (hasSubmissionMarker || hasSubmittedText) { + let checklistBody = checklistComment.body; + + // Extract submission info + const submitterMatch = checklistBody.match(/\*\*Submitted by:\*\* @(\S+)/); + const dateMatch = checklistBody.match(/\*\*Submitted at:\*\* (.+?)(?:\n|UTC)/); + const submitter = submitterMatch ? submitterMatch[1] : 'System'; + const submitDate = dateMatch ? dateMatch[1].trim() : new Date().toLocaleString('en-US', { + timeZone: 'UTC', + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + timeZoneName: 'short' + }); + + // Check if lock section is present and complete + const hasLockMessage = checklistBody.includes('๐Ÿ”’ **This checklist is now locked and cannot be modified.**'); + const hasCompleteLock = hasSubmittedText && hasLockMessage; + + // If lock is missing or incomplete, restore it + if (!hasCompleteLock) { + // Find where to insert/update the lock section + const submitIndex = checklistBody.indexOf('### โœ… **CHECKLIST SUBMITTED**'); + const lockSection = `--- + + ### โœ… **CHECKLIST SUBMITTED** + + **Submitted by:** @${submitter} + **Submitted at:** ${submitDate} (UTC) + + ๐Ÿ”’ **This checklist is now locked and cannot be modified.** + + + + --- + **Note:** This checklist was submitted and is final. No further changes can be made.`; + + if (submitIndex !== -1) { + // Replace from submission header to end of comment + const beforeSubmit = checklistBody.substring(0, submitIndex); + checklistBody = beforeSubmit + lockSection; + } else { + // Append lock section if submission header is missing + checklistBody = checklistBody + '\n\n' + lockSection; + } + + // Re-lock the checklist + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: checklistComment.id, + body: checklistBody + }); + + // Add a warning comment only if this is an edit event (not PR sync) + if (github.event_name == 'issue_comment' && github.event.action == 'edited') { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: `โš ๏ธ **Checklist Re-locked** + + The DevOps Checklist was modified after submission. It has been automatically re-locked. + + ๐Ÿ”’ **This checklist cannot be modified once submitted.**` + }); + } + + console.log('Checklist re-locked after unauthorized edit'); + } + } diff --git a/.github/workflows/devops-checklist.yml b/.github/workflows/devops-checklist.yml index aa6eb8e..0ea9a0a 100644 --- a/.github/workflows/devops-checklist.yml +++ b/.github/workflows/devops-checklist.yml @@ -253,8 +253,9 @@ jobs: ); if (existingComment) { - // Check if already submitted - if (existingComment.body.includes('โœ… **CHECKLIST SUBMITTED**')) { + // Check if already submitted (check both visible marker and hidden marker) + if (existingComment.body.includes('โœ… **CHECKLIST SUBMITTED**') || + existingComment.body.includes('CHECKLIST_SUBMITTED_LOCK')) { console.log('Checklist already submitted and locked, cannot update'); return; } From 0b4e21d947f390cec77b6b3390f5467839bab37f Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Sun, 30 Nov 2025 19:52:01 +0530 Subject: [PATCH 05/21] fix: correct the checklist submission --- .github/workflows/devops-checklist-submit.yml | 97 +++++++++++++++- .github/workflows/devops-checklist.yml | 105 ++++++------------ 2 files changed, 126 insertions(+), 76 deletions(-) diff --git a/.github/workflows/devops-checklist-submit.yml b/.github/workflows/devops-checklist-submit.yml index 89a6820..0630c26 100644 --- a/.github/workflows/devops-checklist-submit.yml +++ b/.github/workflows/devops-checklist-submit.yml @@ -107,6 +107,20 @@ jobs: checklistBody = checklistBody + '\n\n' + submittedSection; } + // Extract and store the exact checkbox states before locking + const checkboxStates = []; + const checkboxRegex = /- \[([ x])\] (.+)/g; + let match; + while ((match = checkboxRegex.exec(checklistBody)) !== null) { + checkboxStates.push({ + checked: match[1] === 'x', + text: match[2] + }); + } + + // Store checkbox states in a hidden comment for restoration + const checkboxStatesJson = JSON.stringify(checkboxStates); + // Update the checklist comment to show it's submitted await github.rest.issues.updateComment({ owner: context.repo.owner, @@ -115,6 +129,43 @@ jobs: body: checklistBody }); + // Add a hidden comment with checkbox states for restoration + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: `` + }); + + // Add label to track submission + try { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + labels: ['devops-checklist-submitted'] + }); + } catch (e) { + // Label might not exist, create it + try { + await github.rest.issues.createLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + name: 'devops-checklist-submitted', + color: '28a745', + description: 'DevOps Checklist has been submitted and locked' + }); + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + labels: ['devops-checklist-submitted'] + }); + } catch (labelError) { + console.log('Could not create/add label:', labelError.message); + } + } + // Add a confirmation comment await github.rest.issues.createComment({ owner: context.repo.owner, @@ -162,10 +213,27 @@ jobs: const hasSubmissionMarker = checklistComment.body.includes('CHECKLIST_SUBMITTED_LOCK'); const hasSubmittedText = checklistComment.body.includes('โœ… **CHECKLIST SUBMITTED**'); - // If it was submitted, ensure it's locked + // If it was submitted, ensure it's locked and restore checkbox states if (hasSubmissionMarker || hasSubmittedText) { let checklistBody = checklistComment.body; + // Get stored checkbox states from hidden comment + let storedStates = null; + const allComments = comments.data; + for (const comment of allComments) { + if (comment.body.includes('CHECKLIST_STATES:')) { + const statesMatch = comment.body.match(/CHECKLIST_STATES:(.+)/); + if (statesMatch) { + try { + storedStates = JSON.parse(statesMatch[1]); + break; + } catch (e) { + console.log('Could not parse stored states'); + } + } + } + } + // Extract submission info const submitterMatch = checklistBody.match(/\*\*Submitted by:\*\* @(\S+)/); const dateMatch = checklistBody.match(/\*\*Submitted at:\*\* (.+?)(?:\n|UTC)/); @@ -181,12 +249,31 @@ jobs: timeZoneName: 'short' }); + // Restore checkbox states if we have stored states + if (storedStates && storedStates.length > 0) { + let restoredBody = checklistBody; + const checkboxRegex = /- \[([ x])\] (.+)/g; + let stateIndex = 0; + + restoredBody = restoredBody.replace(checkboxRegex, (match, checked, text) => { + if (stateIndex < storedStates.length) { + const state = storedStates[stateIndex]; + stateIndex++; + // Restore the original checkbox state + return `- [${state.checked ? 'x' : ' '}] ${state.text}`; + } + return match; + }); + + checklistBody = restoredBody; + } + // Check if lock section is present and complete const hasLockMessage = checklistBody.includes('๐Ÿ”’ **This checklist is now locked and cannot be modified.**'); const hasCompleteLock = hasSubmittedText && hasLockMessage; - // If lock is missing or incomplete, restore it - if (!hasCompleteLock) { + // If lock is missing or incomplete, or checkboxes were changed, restore it + if (!hasCompleteLock || (storedStates && checklistBody !== checklistComment.body)) { // Find where to insert/update the lock section const submitIndex = checklistBody.indexOf('### โœ… **CHECKLIST SUBMITTED**'); const lockSection = `--- @@ -212,7 +299,7 @@ jobs: checklistBody = checklistBody + '\n\n' + lockSection; } - // Re-lock the checklist + // Re-lock the checklist with restored states await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, @@ -228,7 +315,7 @@ jobs: issue_number: prNumber, body: `โš ๏ธ **Checklist Re-locked** - The DevOps Checklist was modified after submission. It has been automatically re-locked. + The DevOps Checklist was modified after submission. It has been automatically re-locked and restored to its submitted state. ๐Ÿ”’ **This checklist cannot be modified once submitted.**` }); diff --git a/.github/workflows/devops-checklist.yml b/.github/workflows/devops-checklist.yml index 0ea9a0a..f65a085 100644 --- a/.github/workflows/devops-checklist.yml +++ b/.github/workflows/devops-checklist.yml @@ -137,22 +137,22 @@ jobs: const fixes = commits.filter(c => c.message.startsWith('fix')).map(c => c.message.replace(/^fix(\(.+?\))?:\s*/i, '')); const other = commits.filter(c => !c.message.startsWith('feat') && !c.message.startsWith('fix') && !c.message.startsWith('chore') && !c.message.startsWith('ci')); - // Build feature details + // Build feature details (without numbering - will be added in formatFeatureDetails) let featureDetails = []; if (features.length > 0) { - featureDetails.push(...features.map(f => `1) ${f}`)); + featureDetails.push(...features); } if (fixes.length > 0) { - featureDetails.push(...fixes.map(f => `2) ${f}`)); + featureDetails.push(...fixes); } if (other.length > 0) { - featureDetails.push(...other.slice(0, 5).map((o, i) => `${i + 3}) ${o.message}`)); + featureDetails.push(...other.slice(0, 5).map(o => o.message)); } const today = new Date().toISOString().split('T')[0]; const releaseDate = today.split('-').reverse().join('-'); // Format: DD-MM-YYYY - // Format feature details better + // Format feature details with sequential numbering const formatFeatureDetails = (details) => { if (details.length === 0) return 'See commits above'; return details.map((f, i) => `${i + 1}) ${f}`).join('
'); @@ -265,80 +265,43 @@ jobs: } // Only create new comment if it doesn't exist - const checklist = `## ๐Ÿ”ง DevOps Checklist - Workflow Review - - **Please review all workflows and checks before merging:** - - ### 1. Workflow Status Review - - [ ] DevOps Checklist Reminder - - [ ] Documentation Required - - [ ] Semantic Commits - - [ ] GitHub Actions Security (send-secrets) - - [ ] Quality Checks / Pre-Commit - - [ ] Quality Checks / Semgrep Rules - - [ ] All other workflows passing - - ### 2. Code Review & Approval - - [ ] Team Lead has approved the PR - - [ ] All reviewer comments resolved - - [ ] No leftover debug logs, temp files, zip files, or accidental commits - - [ ] No exposed secrets or credentials - - [ ] Commit messages follow semantic standards - - ### 3. Documentation & Release Notes - - [ ] Deployment Notes reviewed - - [ ] Change summary provided - - [ ] Impacted modules/services listed - - [ ] Config or environment changes documented - - [ ] Rollback Plan attached - - [ ] ๐Ÿ”— Rollback Reference: https://devops.dhwaniris.com/rollback-guidelines - - ### 4. Functional & QA Validation - - [ ] Changes tested on staging/UAT - - [ ] QA sign-off received - - [ ] No high/critical bugs pending - - [ ] Regression testing completed - - [ ] API responses verified (if applicable) - - ### 5. Security & Compliance - - [ ] Semgrep Rules passed (mandatory) - - [ ] No new security warnings introduced - - [ ] Sensitive logic reviewed - - [ ] No secrets in PR (manual confirmation) - - ### 6. Deployment Verification - - [ ] Deployment strategy identified (Blue/Green / Rolling / Manual) - - [ ] Downtime estimate validated (if applicable) - - [ ] Backup/restore plan ready - - [ ] Monitoring alerts configured or validated - - [ ] Pre-deployment checks completed (DB migrations, queues, cron, cache, etc.) - - ### 7. Merge Validation - - [ ] PR raised from development โ†’ master (correct flow) - - [ ] Branch updated with latest master (no conflicts) - - [ ] All merge blockers cleared - - [ ] No pending tasks or approvals - - ### 8. Final DevOps Decision - - [ ] **GO** โ†’ Safe to merge into production - - [ ] **NO-GO** โ†’ Blocked; failures listed below: + const checklist = `## ๐Ÿ”ง DevOps Checklist + + ### Workflows & Quality + - [ ] All CI/CD workflows passing + - [ ] Code quality checks passed + - [ ] Security scans passed + - [ ] No secrets or credentials exposed + + ### Review & Testing + - [ ] Code review approved + - [ ] Tested on staging/UAT + - [ ] No critical bugs pending + + ### Documentation + - [ ] Deployment notes reviewed + - [ ] Rollback plan documented + - [ ] ๐Ÿ”— [Rollback Guidelines](https://devops.dhwaniris.com/rollback-guidelines) + + ### Deployment Readiness + - [ ] DB migrations ready (if applicable) + - [ ] Config changes documented + - [ ] Monitoring alerts validated + + ### Final Decision + - [ ] **GO** - Ready for production + - [ ] **NO-GO** - Blocked (see notes) **Notes:** \`\`\` - + \`\`\` --- - ### ๐Ÿ“ค Submit Checklist - - **To submit this checklist (cannot be reverted once submitted):** - - Comment: \`/submit-checklist\` - - โš ๏ธ **Warning:** Once submitted, this checklist will be locked and cannot be modified. + **Submit:** Comment \`/submit-checklist\` to lock this checklist - **Status:** โณ *Pending Submission*`; + โš ๏ธ Once submitted, this checklist cannot be modified.`; // Create new comment await github.rest.issues.createComment({ From c9980138565d0d87c2edc2ecf557e6c0d4d55845 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Sun, 30 Nov 2025 20:01:08 +0530 Subject: [PATCH 06/21] fix: added more fixes --- .../devops-checklist-lock-monitor.yml | 175 ++++++++++++++++++ .github/workflows/devops-checklist-submit.yml | 4 +- 2 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/devops-checklist-lock-monitor.yml diff --git a/.github/workflows/devops-checklist-lock-monitor.yml b/.github/workflows/devops-checklist-lock-monitor.yml new file mode 100644 index 0000000..8ddf443 --- /dev/null +++ b/.github/workflows/devops-checklist-lock-monitor.yml @@ -0,0 +1,175 @@ +name: DevOps Checklist Lock Monitor + +on: + schedule: + # Run every 5 minutes to check and re-lock submitted checklists + - cron: '*/5 * * * *' + workflow_dispatch: + pull_request: + types: [synchronize, reopened, ready_for_review, edited] + issue_comment: + types: [created, edited] + +permissions: + pull-requests: write + contents: read + +jobs: + monitor-and-relock: + runs-on: ubuntu-latest + steps: + - name: Monitor and re-lock submitted checklists + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const repo = context.repo; + let prNumbers = []; + + // If triggered by PR event, check that specific PR + if (context.eventName === 'pull_request') { + prNumbers = [context.payload.pull_request.number]; + } else if (context.eventName === 'issue_comment' && context.payload.issue.pull_request) { + prNumbers = [context.payload.issue.number]; + } else { + // For scheduled runs, get all open PRs targeting main/master + const { data: prs } = await github.rest.pulls.list({ + owner: repo.owner, + repo: repo.repo, + state: 'open', + base: 'main', + per_page: 50 + }); + + const { data: masterPRs } = await github.rest.pulls.list({ + owner: repo.owner, + repo: repo.repo, + state: 'open', + base: 'master', + per_page: 50 + }); + + prNumbers = [...prs.map(p => p.number), ...masterPRs.map(p => p.number)]; + } + + for (const prNumber of prNumbers) { + try { + // Get all comments + const { data: comments } = await github.rest.issues.listComments({ + owner: repo.owner, + repo: repo.repo, + issue_number: prNumber, + }); + + // Find checklist comment + const checklistComment = comments.find( + comment => comment.user.type === 'Bot' && + comment.body.includes('DevOps Checklist') + ); + + if (!checklistComment) continue; + + // Check if submitted + const isSubmitted = checklistComment.body.includes('CHECKLIST_SUBMITTED_LOCK') || + checklistComment.body.includes('โœ… **CHECKLIST SUBMITTED**'); + + if (!isSubmitted) continue; + + // Get stored checkbox states + let storedStates = null; + for (const comment of comments) { + if (comment.body.includes('CHECKLIST_STATES:')) { + const match = comment.body.match(/CHECKLIST_STATES:(.+)/); + if (match) { + try { + storedStates = JSON.parse(match[1]); + break; + } catch (e) { + console.log('Could not parse stored states'); + } + } + } + } + + let checklistBody = checklistComment.body; + let needsUpdate = false; + + // Restore checkbox states if we have stored states + if (storedStates && storedStates.length > 0) { + const checkboxRegex = /- \[([ x])\] (.+)/g; + let stateIndex = 0; + let restoredBody = checklistBody; + + restoredBody = restoredBody.replace(checkboxRegex, (match, checked, text) => { + if (stateIndex < storedStates.length) { + const state = storedStates[stateIndex]; + stateIndex++; + const expectedState = state.checked ? 'x' : ' '; + if (checked !== expectedState || text.trim() !== state.text.trim()) { + needsUpdate = true; + return `- [${expectedState}] ${state.text}`; + } + } + return match; + }); + + if (needsUpdate) { + checklistBody = restoredBody; + } + } + + // Ensure lock section is present + if (!checklistBody.includes('๐Ÿ”’ **This checklist is now locked and cannot be modified.**')) { + needsUpdate = true; + const submitterMatch = checklistBody.match(/\*\*Submitted by:\*\* @(\S+)/); + const dateMatch = checklistBody.match(/\*\*Submitted at:\*\* (.+?)(?:\n|UTC)/); + const submitter = submitterMatch ? submitterMatch[1] : 'System'; + const submitDate = dateMatch ? dateMatch[1].trim() : new Date().toLocaleString('en-US', { + timeZone: 'UTC', + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + timeZoneName: 'short' + }); + + const lockSection = `--- + + ### โœ… **CHECKLIST SUBMITTED** + + **Submitted by:** @${submitter} + **Submitted at:** ${submitDate} (UTC) + + ๐Ÿ”’ **This checklist is now locked and cannot be modified.** + + + + --- + **Note:** This checklist was submitted and is final. No further changes can be made.`; + + const submitIndex = checklistBody.indexOf('### โœ… **CHECKLIST SUBMITTED**'); + if (submitIndex !== -1) { + checklistBody = checklistBody.substring(0, submitIndex) + lockSection; + } else { + checklistBody = checklistBody + '\n\n' + lockSection; + } + } + + // Update if needed + if (needsUpdate) { + await github.rest.issues.updateComment({ + owner: repo.owner, + repo: repo.repo, + comment_id: checklistComment.id, + body: checklistBody + }); + + console.log(`Re-locked checklist for PR #${prNumber}`); + } + } catch (error) { + console.log(`Error processing PR #${prNumber}:`, error.message); + } + } + diff --git a/.github/workflows/devops-checklist-submit.yml b/.github/workflows/devops-checklist-submit.yml index 0630c26..da2aad3 100644 --- a/.github/workflows/devops-checklist-submit.yml +++ b/.github/workflows/devops-checklist-submit.yml @@ -56,7 +56,7 @@ jobs: const checklistComment = comments.data.find( comment => comment.user.type === 'Bot' && - comment.body.includes('DevOps Checklist - Workflow Review') + comment.body.includes('DevOps Checklist') ); if (!checklistComment) { @@ -202,7 +202,7 @@ jobs: const checklistComment = comments.data.find( comment => comment.user.type === 'Bot' && - comment.body.includes('DevOps Checklist - Workflow Review') + comment.body.includes('DevOps Checklist') ); if (!checklistComment) { From 775afa96a73c4fa393011c543000df81eefb4ddf Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Sun, 30 Nov 2025 20:08:39 +0530 Subject: [PATCH 07/21] fix: adding quote reply for checklist --- .github/workflows/devops-checklist-submit.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/devops-checklist-submit.yml b/.github/workflows/devops-checklist-submit.yml index da2aad3..ed2e5db 100644 --- a/.github/workflows/devops-checklist-submit.yml +++ b/.github/workflows/devops-checklist-submit.yml @@ -33,6 +33,13 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | + // Check if the command is present (works with quote replies too) + const commentBody = context.payload.comment.body || ''; + if (!commentBody.includes('/submit-checklist')) { + console.log('Submit command not found in comment'); + return; + } + const prNumber = context.payload.issue.number; const submitter = context.payload.comment.user.login; const submitTime = new Date().toISOString(); From b35e14ea0e4b53d960dea4da75b79f6f0a9ee269 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Sun, 30 Nov 2025 20:17:21 +0530 Subject: [PATCH 08/21] feat: changing checklist strategy --- .../devops-checklist-lock-monitor.yml | 175 --------- .github/workflows/devops-checklist-submit.yml | 334 ------------------ .github/workflows/devops-checklist.yml | 20 +- 3 files changed, 13 insertions(+), 516 deletions(-) delete mode 100644 .github/workflows/devops-checklist-lock-monitor.yml delete mode 100644 .github/workflows/devops-checklist-submit.yml diff --git a/.github/workflows/devops-checklist-lock-monitor.yml b/.github/workflows/devops-checklist-lock-monitor.yml deleted file mode 100644 index 8ddf443..0000000 --- a/.github/workflows/devops-checklist-lock-monitor.yml +++ /dev/null @@ -1,175 +0,0 @@ -name: DevOps Checklist Lock Monitor - -on: - schedule: - # Run every 5 minutes to check and re-lock submitted checklists - - cron: '*/5 * * * *' - workflow_dispatch: - pull_request: - types: [synchronize, reopened, ready_for_review, edited] - issue_comment: - types: [created, edited] - -permissions: - pull-requests: write - contents: read - -jobs: - monitor-and-relock: - runs-on: ubuntu-latest - steps: - - name: Monitor and re-lock submitted checklists - uses: actions/github-script@v8 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const repo = context.repo; - let prNumbers = []; - - // If triggered by PR event, check that specific PR - if (context.eventName === 'pull_request') { - prNumbers = [context.payload.pull_request.number]; - } else if (context.eventName === 'issue_comment' && context.payload.issue.pull_request) { - prNumbers = [context.payload.issue.number]; - } else { - // For scheduled runs, get all open PRs targeting main/master - const { data: prs } = await github.rest.pulls.list({ - owner: repo.owner, - repo: repo.repo, - state: 'open', - base: 'main', - per_page: 50 - }); - - const { data: masterPRs } = await github.rest.pulls.list({ - owner: repo.owner, - repo: repo.repo, - state: 'open', - base: 'master', - per_page: 50 - }); - - prNumbers = [...prs.map(p => p.number), ...masterPRs.map(p => p.number)]; - } - - for (const prNumber of prNumbers) { - try { - // Get all comments - const { data: comments } = await github.rest.issues.listComments({ - owner: repo.owner, - repo: repo.repo, - issue_number: prNumber, - }); - - // Find checklist comment - const checklistComment = comments.find( - comment => comment.user.type === 'Bot' && - comment.body.includes('DevOps Checklist') - ); - - if (!checklistComment) continue; - - // Check if submitted - const isSubmitted = checklistComment.body.includes('CHECKLIST_SUBMITTED_LOCK') || - checklistComment.body.includes('โœ… **CHECKLIST SUBMITTED**'); - - if (!isSubmitted) continue; - - // Get stored checkbox states - let storedStates = null; - for (const comment of comments) { - if (comment.body.includes('CHECKLIST_STATES:')) { - const match = comment.body.match(/CHECKLIST_STATES:(.+)/); - if (match) { - try { - storedStates = JSON.parse(match[1]); - break; - } catch (e) { - console.log('Could not parse stored states'); - } - } - } - } - - let checklistBody = checklistComment.body; - let needsUpdate = false; - - // Restore checkbox states if we have stored states - if (storedStates && storedStates.length > 0) { - const checkboxRegex = /- \[([ x])\] (.+)/g; - let stateIndex = 0; - let restoredBody = checklistBody; - - restoredBody = restoredBody.replace(checkboxRegex, (match, checked, text) => { - if (stateIndex < storedStates.length) { - const state = storedStates[stateIndex]; - stateIndex++; - const expectedState = state.checked ? 'x' : ' '; - if (checked !== expectedState || text.trim() !== state.text.trim()) { - needsUpdate = true; - return `- [${expectedState}] ${state.text}`; - } - } - return match; - }); - - if (needsUpdate) { - checklistBody = restoredBody; - } - } - - // Ensure lock section is present - if (!checklistBody.includes('๐Ÿ”’ **This checklist is now locked and cannot be modified.**')) { - needsUpdate = true; - const submitterMatch = checklistBody.match(/\*\*Submitted by:\*\* @(\S+)/); - const dateMatch = checklistBody.match(/\*\*Submitted at:\*\* (.+?)(?:\n|UTC)/); - const submitter = submitterMatch ? submitterMatch[1] : 'System'; - const submitDate = dateMatch ? dateMatch[1].trim() : new Date().toLocaleString('en-US', { - timeZone: 'UTC', - year: 'numeric', - month: 'long', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - timeZoneName: 'short' - }); - - const lockSection = `--- - - ### โœ… **CHECKLIST SUBMITTED** - - **Submitted by:** @${submitter} - **Submitted at:** ${submitDate} (UTC) - - ๐Ÿ”’ **This checklist is now locked and cannot be modified.** - - - - --- - **Note:** This checklist was submitted and is final. No further changes can be made.`; - - const submitIndex = checklistBody.indexOf('### โœ… **CHECKLIST SUBMITTED**'); - if (submitIndex !== -1) { - checklistBody = checklistBody.substring(0, submitIndex) + lockSection; - } else { - checklistBody = checklistBody + '\n\n' + lockSection; - } - } - - // Update if needed - if (needsUpdate) { - await github.rest.issues.updateComment({ - owner: repo.owner, - repo: repo.repo, - comment_id: checklistComment.id, - body: checklistBody - }); - - console.log(`Re-locked checklist for PR #${prNumber}`); - } - } catch (error) { - console.log(`Error processing PR #${prNumber}:`, error.message); - } - } - diff --git a/.github/workflows/devops-checklist-submit.yml b/.github/workflows/devops-checklist-submit.yml deleted file mode 100644 index ed2e5db..0000000 --- a/.github/workflows/devops-checklist-submit.yml +++ /dev/null @@ -1,334 +0,0 @@ -name: DevOps Checklist Submission - -on: - issue_comment: - types: [created, edited] - pull_request: - types: [synchronize, reopened, ready_for_review, closed] - -permissions: - pull-requests: write - contents: read - -jobs: - submit-checklist: - runs-on: ubuntu-latest - if: | - (github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '/submit-checklist')) || - (github.event_name == 'pull_request' && (github.event.action == 'synchronize' || github.event.action == 'reopened' || github.event.action == 'ready_for_review' || github.event.action == 'closed')) - steps: - - name: Get PR number - id: get-pr - uses: actions/github-script@v8 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const prNumber = context.payload.issue.number; - core.setOutput('pr_number', prNumber); - return prNumber; - - - name: Submit and lock checklist - if: github.event_name == 'issue_comment' && contains(github.event.comment.body, '/submit-checklist') - uses: actions/github-script@v8 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - // Check if the command is present (works with quote replies too) - const commentBody = context.payload.comment.body || ''; - if (!commentBody.includes('/submit-checklist')) { - console.log('Submit command not found in comment'); - return; - } - - const prNumber = context.payload.issue.number; - const submitter = context.payload.comment.user.login; - const submitTime = new Date().toISOString(); - const submitDate = new Date().toLocaleString('en-US', { - timeZone: 'UTC', - year: 'numeric', - month: 'long', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - timeZoneName: 'short' - }); - - // Get all comments to find the checklist - const comments = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - }); - - const checklistComment = comments.data.find( - comment => comment.user.type === 'Bot' && - comment.body.includes('DevOps Checklist') - ); - - if (!checklistComment) { - console.log('Checklist comment not found'); - return; - } - - // Check if already submitted - if (checklistComment.body.includes('โœ… **CHECKLIST SUBMITTED**')) { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body: `โš ๏ธ This checklist has already been submitted and cannot be modified.` - }); - return; - } - - // Extract the current checklist content (preserve checkboxes) - let checklistBody = checklistComment.body; - - // Extract submission info to preserve it - const submissionInfo = `**Submitted by:** @${submitter} - **Submitted at:** ${submitDate} (UTC)`; - - // Replace the submission section with submitted status - const submittedSection = `--- - - ### โœ… **CHECKLIST SUBMITTED** - - ${submissionInfo} - - ๐Ÿ”’ **This checklist is now locked and cannot be modified.** - - - - --- - **Note:** This checklist was submitted and is final. No further changes can be made.`; - - // Find and replace the submission section - // Look for the "Submit Checklist" section and replace everything from there to the end - const submitSectionStart = checklistBody.indexOf('### ๐Ÿ“ค Submit Checklist'); - if (submitSectionStart !== -1) { - // Keep everything before the submission section, then add the submitted section - checklistBody = checklistBody.substring(0, submitSectionStart) + submittedSection; - } else { - // If section not found, append the submitted section - checklistBody = checklistBody + '\n\n' + submittedSection; - } - - // Extract and store the exact checkbox states before locking - const checkboxStates = []; - const checkboxRegex = /- \[([ x])\] (.+)/g; - let match; - while ((match = checkboxRegex.exec(checklistBody)) !== null) { - checkboxStates.push({ - checked: match[1] === 'x', - text: match[2] - }); - } - - // Store checkbox states in a hidden comment for restoration - const checkboxStatesJson = JSON.stringify(checkboxStates); - - // Update the checklist comment to show it's submitted - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: checklistComment.id, - body: checklistBody - }); - - // Add a hidden comment with checkbox states for restoration - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body: `` - }); - - // Add label to track submission - try { - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - labels: ['devops-checklist-submitted'] - }); - } catch (e) { - // Label might not exist, create it - try { - await github.rest.issues.createLabel({ - owner: context.repo.owner, - repo: context.repo.repo, - name: 'devops-checklist-submitted', - color: '28a745', - description: 'DevOps Checklist has been submitted and locked' - }); - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - labels: ['devops-checklist-submitted'] - }); - } catch (labelError) { - console.log('Could not create/add label:', labelError.message); - } - } - - // Add a confirmation comment - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body: `โœ… **Checklist submitted successfully!** - - The DevOps Checklist has been locked and cannot be modified. - - **Submitted by:** @${submitter} - **Time:** ${submitDate} (UTC)` - }); - - console.log(`Checklist submitted by ${submitter} at ${submitTime}`); - - - name: Re-lock checklist if edited after submission - if: | - (github.event_name == 'issue_comment' && github.event.action == 'edited') || - (github.event_name == 'pull_request') - uses: actions/github-script@v8 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const prNumber = github.event_name == 'issue_comment' - ? context.payload.issue.number - : context.payload.pull_request.number; - - // Get all comments to find the checklist - const comments = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - }); - - const checklistComment = comments.data.find( - comment => comment.user.type === 'Bot' && - comment.body.includes('DevOps Checklist') - ); - - if (!checklistComment) { - return; - } - - // Check if checklist was previously submitted (look for submission marker) - const hasSubmissionMarker = checklistComment.body.includes('CHECKLIST_SUBMITTED_LOCK'); - const hasSubmittedText = checklistComment.body.includes('โœ… **CHECKLIST SUBMITTED**'); - - // If it was submitted, ensure it's locked and restore checkbox states - if (hasSubmissionMarker || hasSubmittedText) { - let checklistBody = checklistComment.body; - - // Get stored checkbox states from hidden comment - let storedStates = null; - const allComments = comments.data; - for (const comment of allComments) { - if (comment.body.includes('CHECKLIST_STATES:')) { - const statesMatch = comment.body.match(/CHECKLIST_STATES:(.+)/); - if (statesMatch) { - try { - storedStates = JSON.parse(statesMatch[1]); - break; - } catch (e) { - console.log('Could not parse stored states'); - } - } - } - } - - // Extract submission info - const submitterMatch = checklistBody.match(/\*\*Submitted by:\*\* @(\S+)/); - const dateMatch = checklistBody.match(/\*\*Submitted at:\*\* (.+?)(?:\n|UTC)/); - const submitter = submitterMatch ? submitterMatch[1] : 'System'; - const submitDate = dateMatch ? dateMatch[1].trim() : new Date().toLocaleString('en-US', { - timeZone: 'UTC', - year: 'numeric', - month: 'long', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - timeZoneName: 'short' - }); - - // Restore checkbox states if we have stored states - if (storedStates && storedStates.length > 0) { - let restoredBody = checklistBody; - const checkboxRegex = /- \[([ x])\] (.+)/g; - let stateIndex = 0; - - restoredBody = restoredBody.replace(checkboxRegex, (match, checked, text) => { - if (stateIndex < storedStates.length) { - const state = storedStates[stateIndex]; - stateIndex++; - // Restore the original checkbox state - return `- [${state.checked ? 'x' : ' '}] ${state.text}`; - } - return match; - }); - - checklistBody = restoredBody; - } - - // Check if lock section is present and complete - const hasLockMessage = checklistBody.includes('๐Ÿ”’ **This checklist is now locked and cannot be modified.**'); - const hasCompleteLock = hasSubmittedText && hasLockMessage; - - // If lock is missing or incomplete, or checkboxes were changed, restore it - if (!hasCompleteLock || (storedStates && checklistBody !== checklistComment.body)) { - // Find where to insert/update the lock section - const submitIndex = checklistBody.indexOf('### โœ… **CHECKLIST SUBMITTED**'); - const lockSection = `--- - - ### โœ… **CHECKLIST SUBMITTED** - - **Submitted by:** @${submitter} - **Submitted at:** ${submitDate} (UTC) - - ๐Ÿ”’ **This checklist is now locked and cannot be modified.** - - - - --- - **Note:** This checklist was submitted and is final. No further changes can be made.`; - - if (submitIndex !== -1) { - // Replace from submission header to end of comment - const beforeSubmit = checklistBody.substring(0, submitIndex); - checklistBody = beforeSubmit + lockSection; - } else { - // Append lock section if submission header is missing - checklistBody = checklistBody + '\n\n' + lockSection; - } - - // Re-lock the checklist with restored states - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: checklistComment.id, - body: checklistBody - }); - - // Add a warning comment only if this is an edit event (not PR sync) - if (github.event_name == 'issue_comment' && github.event.action == 'edited') { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body: `โš ๏ธ **Checklist Re-locked** - - The DevOps Checklist was modified after submission. It has been automatically re-locked and restored to its submitted state. - - ๐Ÿ”’ **This checklist cannot be modified once submitted.**` - }); - } - - console.log('Checklist re-locked after unauthorized edit'); - } - } - diff --git a/.github/workflows/devops-checklist.yml b/.github/workflows/devops-checklist.yml index f65a085..8aa6c35 100644 --- a/.github/workflows/devops-checklist.yml +++ b/.github/workflows/devops-checklist.yml @@ -265,7 +265,14 @@ jobs: } // Only create new comment if it doesn't exist - const checklist = `## ๐Ÿ”ง DevOps Checklist + const checklist = `## ๐Ÿ”ง DevOps Checklist Template + + **DevOps Team: Copy the template below, fill it out, and post it as a comment on this PR.** + + --- + + \`\`\` + ## ๐Ÿ”ง DevOps Checklist ### Workflows & Quality - [ ] All CI/CD workflows passing @@ -293,15 +300,14 @@ jobs: - [ ] **NO-GO** - Blocked (see notes) **Notes:** - \`\`\` - + + + **Reviewed by:** @username + **Date:** YYYY-MM-DD \`\`\` --- - - **Submit:** Comment \`/submit-checklist\` to lock this checklist - - โš ๏ธ Once submitted, this checklist cannot be modified.`; + *Copy the template above, fill it out with your review, and post it as a comment.*`; // Create new comment await github.rest.issues.createComment({ From 36b45c8ba0975b6ef3450e049c7fcfb1dd11857c Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Sun, 30 Nov 2025 20:26:10 +0530 Subject: [PATCH 09/21] fix: improving checklist --- .github/workflows/devops-checklist.yml | 32 +++++--------------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/.github/workflows/devops-checklist.yml b/.github/workflows/devops-checklist.yml index 8aa6c35..0779ec0 100644 --- a/.github/workflows/devops-checklist.yml +++ b/.github/workflows/devops-checklist.yml @@ -267,47 +267,27 @@ jobs: // Only create new comment if it doesn't exist const checklist = `## ๐Ÿ”ง DevOps Checklist Template - **DevOps Team: Copy the template below, fill it out, and post it as a comment on this PR.** + ๐Ÿ‘‹ **DevOps Team:** Copy the template below, fill it out, and post it as a comment on this PR. --- - \`\`\` + \`\`\`markdown ## ๐Ÿ”ง DevOps Checklist - ### Workflows & Quality + ### โœ… Workflows & Quality - [ ] All CI/CD workflows passing - [ ] Code quality checks passed - [ ] Security scans passed - [ ] No secrets or credentials exposed - ### Review & Testing - - [ ] Code review approved - - [ ] Tested on staging/UAT - - [ ] No critical bugs pending - - ### Documentation + ### ๐Ÿ“ Documentation - [ ] Deployment notes reviewed - - [ ] Rollback plan documented - [ ] ๐Ÿ”— [Rollback Guidelines](https://devops.dhwaniris.com/rollback-guidelines) - - ### Deployment Readiness - - [ ] DB migrations ready (if applicable) - - [ ] Config changes documented - - [ ] Monitoring alerts validated - - ### Final Decision - - [ ] **GO** - Ready for production - - [ ] **NO-GO** - Blocked (see notes) - - **Notes:** - - - **Reviewed by:** @username - **Date:** YYYY-MM-DD \`\`\` --- - *Copy the template above, fill it out with your review, and post it as a comment.*`; + + ๐Ÿ’ก **Instructions:** Copy the template above, check the boxes (replace \`[ ]\` with \`[x]\`), and post it as a comment.`; // Create new comment await github.rest.issues.createComment({ From 153ed8707ceb2ebeb05ac2f907fd4d44e3c40837 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Sun, 30 Nov 2025 20:30:52 +0530 Subject: [PATCH 10/21] fix: improving checklist --- .github/workflows/add-pr-to-devops.yml | 57 ++++++++++++++++++++++++++ .github/workflows/devops-checklist.yml | 10 ++--- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/.github/workflows/add-pr-to-devops.yml b/.github/workflows/add-pr-to-devops.yml index 08b6733..2a371ac 100644 --- a/.github/workflows/add-pr-to-devops.yml +++ b/.github/workflows/add-pr-to-devops.yml @@ -22,4 +22,61 @@ jobs: with: project-url: https://github.com/orgs/dhwani-ris/projects/## github-token: ${{ secrets.GITHUB_TOKEN }} + content-id: ${{ github.event.pull_request.node_id }} + content-type: PullRequest + continue-on-error: true + + - name: Alternative - Add PR using GitHub API + if: failure() + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + // Alternative method using GitHub API + const prNodeId = context.payload.pull_request.node_id; + const projectNumber = '##'; // Replace with actual project number + + try { + // Get organization projects + const { data: projects } = await github.rest.projects.listForOrg({ + org: 'dhwani-ris', + state: 'open' + }); + + // Find the DevOps Release & QC Board project + const devopsProject = projects.find(p => + p.name.includes('DevOps') || + p.name.includes('Release') || + p.name.includes('QC') + ); + + if (!devopsProject) { + console.log('DevOps project not found'); + return; + } + + // Get project columns + const { data: columns } = await github.rest.projects.listColumns({ + project_id: devopsProject.id + }); + + if (columns.length === 0) { + console.log('No columns found in project'); + return; + } + + // Add PR to the first column (usually "To do" or "In progress") + const firstColumn = columns[0]; + + await github.rest.projects.createCard({ + column_id: firstColumn.id, + content_id: prNodeId, + content_type: 'PullRequest' + }); + + console.log(`โœ… Added PR #${context.payload.pull_request.number} to DevOps board`); + } catch (error) { + console.log('Error adding to project:', error.message); + console.log('Note: Make sure GITHUB_TOKEN has organization project permissions or use a PAT'); + } diff --git a/.github/workflows/devops-checklist.yml b/.github/workflows/devops-checklist.yml index 0779ec0..49b02dc 100644 --- a/.github/workflows/devops-checklist.yml +++ b/.github/workflows/devops-checklist.yml @@ -265,15 +265,12 @@ jobs: } // Only create new comment if it doesn't exist - const checklist = `## ๐Ÿ”ง DevOps Checklist Template + const checklist = `## ๐Ÿ”ง DevOps Checklist - ๐Ÿ‘‹ **DevOps Team:** Copy the template below, fill it out, and post it as a comment on this PR. + ๐Ÿ‘‹ **DevOps Team:** Please review and check the items below. --- - \`\`\`markdown - ## ๐Ÿ”ง DevOps Checklist - ### โœ… Workflows & Quality - [ ] All CI/CD workflows passing - [ ] Code quality checks passed @@ -283,11 +280,10 @@ jobs: ### ๐Ÿ“ Documentation - [ ] Deployment notes reviewed - [ ] ๐Ÿ”— [Rollback Guidelines](https://devops.dhwaniris.com/rollback-guidelines) - \`\`\` --- - ๐Ÿ’ก **Instructions:** Copy the template above, check the boxes (replace \`[ ]\` with \`[x]\`), and post it as a comment.`; + ๐Ÿ’ก *Click the checkboxes above to mark items as complete.*`; // Create new comment await github.rest.issues.createComment({ From 1966ecfdba7243d337b47f007c2542aeec5f6ba9 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Sun, 30 Nov 2025 20:49:27 +0530 Subject: [PATCH 11/21] feat: automatic pr adds to org project devops qc --- .github/workflows/add-pr-to-devops.yml | 41 ++++++++++++++------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/.github/workflows/add-pr-to-devops.yml b/.github/workflows/add-pr-to-devops.yml index 2a371ac..8862f1d 100644 --- a/.github/workflows/add-pr-to-devops.yml +++ b/.github/workflows/add-pr-to-devops.yml @@ -18,23 +18,12 @@ jobs: organization-projects: write steps: - name: Add PR to DevOps Release Board - uses: actions/add-to-project@v0.5.0 - with: - project-url: https://github.com/orgs/dhwani-ris/projects/## - github-token: ${{ secrets.GITHUB_TOKEN }} - content-id: ${{ github.event.pull_request.node_id }} - content-type: PullRequest - continue-on-error: true - - - name: Alternative - Add PR using GitHub API - if: failure() uses: actions/github-script@v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - // Alternative method using GitHub API const prNodeId = context.payload.pull_request.node_id; - const projectNumber = '##'; // Replace with actual project number + const prNumber = context.payload.pull_request.number; try { // Get organization projects @@ -43,18 +32,22 @@ jobs: state: 'open' }); + console.log(`Found ${projects.length} organization projects`); + // Find the DevOps Release & QC Board project const devopsProject = projects.find(p => - p.name.includes('DevOps') || - p.name.includes('Release') || - p.name.includes('QC') + p.name.toLowerCase().includes('devops') && + (p.name.toLowerCase().includes('release') || p.name.toLowerCase().includes('qc')) ); if (!devopsProject) { - console.log('DevOps project not found'); + console.log('DevOps Release & QC Board project not found'); + console.log('Available projects:', projects.map(p => p.name).join(', ')); return; } + console.log(`Found project: ${devopsProject.name} (ID: ${devopsProject.id})`); + // Get project columns const { data: columns } = await github.rest.projects.listColumns({ project_id: devopsProject.id @@ -65,6 +58,8 @@ jobs: return; } + console.log(`Found ${columns.length} columns:`, columns.map(c => c.name).join(', ')); + // Add PR to the first column (usually "To do" or "In progress") const firstColumn = columns[0]; @@ -74,9 +69,17 @@ jobs: content_type: 'PullRequest' }); - console.log(`โœ… Added PR #${context.payload.pull_request.number} to DevOps board`); + console.log(`โœ… Successfully added PR #${prNumber} to DevOps Release & QC Board`); + console.log(` Project: ${devopsProject.name}`); + console.log(` Column: ${firstColumn.name}`); } catch (error) { - console.log('Error adding to project:', error.message); - console.log('Note: Make sure GITHUB_TOKEN has organization project permissions or use a PAT'); + console.log('โŒ Error adding PR to project:', error.message); + if (error.status === 403) { + console.log('โš ๏ธ Permission denied. GITHUB_TOKEN may not have organization project access.'); + console.log(' Consider using a Personal Access Token (PAT) with project scope.'); + } else if (error.status === 404) { + console.log('โš ๏ธ Project or column not found. Check project name and permissions.'); + } + throw error; } From 3108df55fca325b5874f625737db9f3a22504423 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Sun, 30 Nov 2025 23:43:40 +0530 Subject: [PATCH 12/21] fix: improving checklist --- .github/workflows/devops-checklist.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/devops-checklist.yml b/.github/workflows/devops-checklist.yml index 49b02dc..ba7ea48 100644 --- a/.github/workflows/devops-checklist.yml +++ b/.github/workflows/devops-checklist.yml @@ -271,15 +271,15 @@ jobs: --- - ### โœ… Workflows & Quality - - [ ] All CI/CD workflows passing - - [ ] Code quality checks passed - - [ ] Security scans passed - - [ ] No secrets or credentials exposed + ### โœ… Pre-Merge Verification + - [ ] All CI/CD workflows passing (check Actions tab) + - [ ] Code quality checks passed (Semgrep, Pre-commit) + - [ ] Security scans passed (no vulnerabilities) + - [ ] No secrets or credentials exposed (manual review) ### ๐Ÿ“ Documentation - - [ ] Deployment notes reviewed - - [ ] ๐Ÿ”— [Rollback Guidelines](https://devops.dhwaniris.com/rollback-guidelines) + - [ ] Deployment notes reviewed (see comment above) + - [ ] ๐Ÿ”— [Rollback Guidelines](https://dhwaniris1-my.sharepoint.com/:b:/g/personal/technology_dhwaniris_com/IQBZ-x3H8jIjQoliD_JEKHfSAQq2pMXdy8wFAdISg7fInTE?e=gd7dsq) reviewed --- From 3ce36d83fa176e1ae6c996a47f3675e1c599b0b2 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Mon, 1 Dec 2025 00:00:15 +0530 Subject: [PATCH 13/21] fix: project viisbility --- .github/workflows/add-pr-to-devops.yml | 81 ++++++++++++++++++++------ 1 file changed, 64 insertions(+), 17 deletions(-) diff --git a/.github/workflows/add-pr-to-devops.yml b/.github/workflows/add-pr-to-devops.yml index 8862f1d..3a5d4da 100644 --- a/.github/workflows/add-pr-to-devops.yml +++ b/.github/workflows/add-pr-to-devops.yml @@ -24,6 +24,9 @@ jobs: script: | const prNodeId = context.payload.pull_request.node_id; const prNumber = context.payload.pull_request.number; + const prUrl = context.payload.pull_request.html_url; + + console.log(`Processing PR #${prNumber} (Node ID: ${prNodeId})`); try { // Get organization projects @@ -34,19 +37,37 @@ jobs: console.log(`Found ${projects.length} organization projects`); - // Find the DevOps Release & QC Board project - const devopsProject = projects.find(p => - p.name.toLowerCase().includes('devops') && - (p.name.toLowerCase().includes('release') || p.name.toLowerCase().includes('qc')) - ); + if (projects.length === 0) { + console.log('โš ๏ธ No organization projects found. Check permissions.'); + return; + } + + // Log all project names for debugging + console.log('Available projects:', projects.map(p => `"${p.name}" (ID: ${p.id})`).join(', ')); + + // Find the DevOps Release & QC Board project - try multiple name patterns + let devopsProject = projects.find(p => { + const name = p.name.toLowerCase(); + return (name.includes('devops') && name.includes('release')) || + (name.includes('devops') && name.includes('qc')) || + (name.includes('devops') && name.includes('board')); + }); + + // If not found, try just "devops" + if (!devopsProject) { + devopsProject = projects.find(p => + p.name.toLowerCase().includes('devops') + ); + } if (!devopsProject) { - console.log('DevOps Release & QC Board project not found'); - console.log('Available projects:', projects.map(p => p.name).join(', ')); + console.log('โŒ DevOps Release & QC Board project not found'); + console.log('Available project names:', projects.map(p => p.name).join(', ')); + console.log('Please check the project name matches: "DevOps", "Release", or "QC"'); return; } - console.log(`Found project: ${devopsProject.name} (ID: ${devopsProject.id})`); + console.log(`โœ… Found project: "${devopsProject.name}" (ID: ${devopsProject.id})`); // Get project columns const { data: columns } = await github.rest.projects.listColumns({ @@ -54,15 +75,31 @@ jobs: }); if (columns.length === 0) { - console.log('No columns found in project'); + console.log('โŒ No columns found in project'); return; } - console.log(`Found ${columns.length} columns:`, columns.map(c => c.name).join(', ')); + console.log(`Found ${columns.length} columns:`, columns.map(c => `"${c.name}"`).join(', ')); - // Add PR to the first column (usually "To do" or "In progress") + // Find the first column (usually "To do", "In progress", or "Backlog") const firstColumn = columns[0]; + console.log(`Adding PR to column: "${firstColumn.name}"`); + // Check if PR is already in the project + const { data: cards } = await github.rest.projects.listCards({ + column_id: firstColumn.id + }); + + const alreadyAdded = cards.some(card => + card.content_url && card.content_url.includes(`/pulls/${prNumber}`) + ); + + if (alreadyAdded) { + console.log(`โœ… PR #${prNumber} is already in the project`); + return; + } + + // Add PR to the first column await github.rest.projects.createCard({ column_id: firstColumn.id, content_id: prNodeId, @@ -70,16 +107,26 @@ jobs: }); console.log(`โœ… Successfully added PR #${prNumber} to DevOps Release & QC Board`); - console.log(` Project: ${devopsProject.name}`); - console.log(` Column: ${firstColumn.name}`); + console.log(` Project: "${devopsProject.name}"`); + console.log(` Column: "${firstColumn.name}"`); + console.log(` PR URL: ${prUrl}`); } catch (error) { console.log('โŒ Error adding PR to project:', error.message); + console.log('Error details:', JSON.stringify(error, null, 2)); + if (error.status === 403) { - console.log('โš ๏ธ Permission denied. GITHUB_TOKEN may not have organization project access.'); - console.log(' Consider using a Personal Access Token (PAT) with project scope.'); + console.log('โš ๏ธ Permission denied (403). GITHUB_TOKEN may not have organization project access.'); + console.log(' Solution: Use a Personal Access Token (PAT) with "project" scope.'); + console.log(' 1. Create PAT at: https://github.com/settings/tokens'); + console.log(' 2. Add it as secret: PROJECT_TOKEN'); + console.log(' 3. Update workflow to use: ${{ secrets.PROJECT_TOKEN }}'); } else if (error.status === 404) { - console.log('โš ๏ธ Project or column not found. Check project name and permissions.'); + console.log('โš ๏ธ Not found (404). Check project name and permissions.'); + } else if (error.status === 401) { + console.log('โš ๏ธ Unauthorized (401). Token may be invalid or expired.'); } - throw error; + + // Don't fail the workflow, just log the error + console.log('โš ๏ธ Continuing workflow despite error...'); } From 0db509b27ae75173c6981fb3067ff40467a1a24e Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Mon, 1 Dec 2025 00:03:45 +0530 Subject: [PATCH 14/21] fix: project viisbility --- .github/workflows/add-pr-to-devops.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/add-pr-to-devops.yml b/.github/workflows/add-pr-to-devops.yml index 3a5d4da..a1fcc74 100644 --- a/.github/workflows/add-pr-to-devops.yml +++ b/.github/workflows/add-pr-to-devops.yml @@ -20,7 +20,7 @@ jobs: - name: Add PR to DevOps Release Board uses: actions/github-script@v8 with: - github-token: ${{ secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.PROJECT_TOKEN || secrets.GITHUB_TOKEN }} script: | const prNodeId = context.payload.pull_request.node_id; const prNumber = context.payload.pull_request.number; @@ -45,12 +45,17 @@ jobs: // Log all project names for debugging console.log('Available projects:', projects.map(p => `"${p.name}" (ID: ${p.id})`).join(', ')); - // Find the DevOps Release & QC Board project - try multiple name patterns + // Find the DevOps Release & QC Board project - try exact match first + // Project name from UI: "Dhwani - DevOps Release & QC Board" let devopsProject = projects.find(p => { const name = p.name.toLowerCase(); - return (name.includes('devops') && name.includes('release')) || - (name.includes('devops') && name.includes('qc')) || - (name.includes('devops') && name.includes('board')); + // Try exact match or close match + return name === 'dhwani - devops release & qc board' || + name === 'dhwani โ€“ devops release & qc board' || // Different dash character + (name.includes('dhwani') && name.includes('devops') && name.includes('release') && name.includes('qc')) || + (name.includes('devops') && name.includes('release') && name.includes('qc')) || + (name.includes('devops') && name.includes('release')) || + (name.includes('devops') && name.includes('qc')); }); // If not found, try just "devops" From 3098d87cd656f9678c12269f90da968835812e00 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Mon, 1 Dec 2025 11:26:17 +0530 Subject: [PATCH 15/21] feat: adding OWASP Dependency-Check and CODEOWNER to devops team --- .github/CODEOWNERS | 14 ++++++++ .github/workflows/security-scan.yml | 52 +++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..a33bba7 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,14 @@ +# CODEOWNERS file +# This file defines code owners for the repository +# See: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# DevOps team owns all files +* @dhwani-ris/dhwani-devops + +# GitHub workflows - DevOps team +/.github/workflows/ @dhwani-ris/dhwani-devops + +# Configuration files - DevOps team +/.github/ @dhwani-ris/dhwani-devops +/.releaserc @dhwani-ris/dhwani-devops + diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 491df7b..d7822ff 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -82,6 +82,26 @@ jobs: with: python-version: '3.13' + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Run OWASP Dependency-Check + uses: dependency-check/Dependency-Check_Action@main + id: depcheck + with: + project: 'gatepass-check' + path: '.' + format: 'JSON' + args: > + --enableRetired + --enableExperimental + --failOnCVSS 7 + --out reports + token: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true + - name: Run pip-audit run: | pip install pip-audit @@ -102,6 +122,7 @@ jobs: with: name: security-reports-${{ github.run_id }} path: | + reports/dependency-check-report.json pip-audit-report.json safety-report.json bandit-report.json @@ -119,6 +140,31 @@ jobs: let comment = '## ๐Ÿ”’ Security Scan Results\n\n'; let hasIssues = false; + // Check OWASP Dependency-Check + try { + if (fs.existsSync('reports/dependency-check-report.json')) { + const report = JSON.parse(fs.readFileSync('reports/dependency-check-report.json', 'utf8')); + if (report.dependencies && report.dependencies.length > 0) { + const vulnerableDeps = report.dependencies.filter(dep => + dep.vulnerabilities && dep.vulnerabilities.length > 0 + ); + if (vulnerableDeps.length > 0) { + hasIssues = true; + comment += `### โš ๏ธ OWASP Dependency-Check Findings\n\n`; + comment += `Found ${vulnerableDeps.length} vulnerable dependency(ies) with ${vulnerableDeps.reduce((sum, d) => sum + d.vulnerabilities.length, 0)} total vulnerability(ies).\n\n`; + vulnerableDeps.slice(0, 10).forEach(dep => { + const cves = dep.vulnerabilities.map(v => v.name).join(', '); + const severity = dep.vulnerabilities.map(v => v.severity || 'UNKNOWN').join(', '); + comment += `- **${dep.fileName || dep.packagePath}**: ${cves} (Severity: ${severity})\n`; + }); + comment += `\n`; + } + } + } + } catch (e) { + console.log('Error reading OWASP Dependency-Check report:', e); + } + // Check pip-audit try { if (fs.existsSync('pip-audit-report.json')) { @@ -208,7 +254,7 @@ jobs: security-summary: name: Security Summary runs-on: ubuntu-latest - needs: [codeql-analysis, secret-scanning, dependency-scanning] + needs: [codeql-analysis, secret-scanning, dependency-scanning, container-scanning] if: always() steps: @@ -224,7 +270,9 @@ jobs: |-----------|--------| | CodeQL Analysis | ${needs.codeql-analysis.result === 'success' ? 'โœ… Passed' : needs.codeql-analysis.result === 'failure' ? 'โŒ Failed' : 'โš ๏ธ Skipped'} | | Secret Scanning | ${needs.secret-scanning.result === 'success' ? 'โœ… Passed' : needs.secret-scanning.result === 'failure' ? 'โŒ Failed' : 'โš ๏ธ Skipped'} | - | Dependency Scan | ${needs.dependency-scanning.result === 'success' ? 'โœ… Passed' : needs.dependency-scanning.result === 'failure' ? 'โŒ Failed' : 'โš ๏ธ Skipped'} | + | OWASP Dependency-Check | ${needs.dependency-scanning.result === 'success' ? 'โœ… Passed' : needs.dependency-scanning.result === 'failure' ? 'โŒ Failed' : 'โš ๏ธ Skipped'} | + | Dependency Scan (pip-audit/safety) | ${needs.dependency-scanning.result === 'success' ? 'โœ… Passed' : needs.dependency-scanning.result === 'failure' ? 'โŒ Failed' : 'โš ๏ธ Skipped'} | + | Container Scan (Trivy) | ${needs.container-scanning && needs.container-scanning.result === 'success' ? 'โœ… Passed' : needs.container-scanning && needs.container-scanning.result === 'failure' ? 'โŒ Failed' : 'โš ๏ธ Skipped'} | **View detailed results in the Security tab or workflow artifacts.**`; From c863dee05af94372731a209d6103798722b72117 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Mon, 1 Dec 2025 11:33:44 +0530 Subject: [PATCH 16/21] fix: fixing adding project and review thing --- .github/workflows/add-pr-to-devops.yml | 77 +++++++++++++++----------- .github/workflows/auto-reviewer.yml | 51 +++++++++++------ 2 files changed, 81 insertions(+), 47 deletions(-) diff --git a/.github/workflows/add-pr-to-devops.yml b/.github/workflows/add-pr-to-devops.yml index a1fcc74..dc3608f 100644 --- a/.github/workflows/add-pr-to-devops.yml +++ b/.github/workflows/add-pr-to-devops.yml @@ -2,15 +2,17 @@ name: Add PR to DevOps Board on: pull_request: - types: [opened, reopened] - branches: [main, master] + types: [opened, reopened, synchronize] + branches: [main, master, develop, development] jobs: add_to_project: runs-on: ubuntu-latest if: | github.event.pull_request.base.ref == 'main' || - github.event.pull_request.base.ref == 'master' + github.event.pull_request.base.ref == 'master' || + github.event.pull_request.base.ref == 'develop' || + github.event.pull_request.base.ref == 'development' permissions: contents: read pull-requests: write @@ -46,14 +48,19 @@ jobs: console.log('Available projects:', projects.map(p => `"${p.name}" (ID: ${p.id})`).join(', ')); // Find the DevOps Release & QC Board project - try exact match first - // Project name from UI: "Dhwani - DevOps Release & QC Board" + // Project name from UI: "Dhwani โ€“ DevOps Release & QC Board" (with en-dash) let devopsProject = projects.find(p => { const name = p.name.toLowerCase(); - // Try exact match or close match + // Try exact match with various dash characters (hyphen, en-dash, em-dash) return name === 'dhwani - devops release & qc board' || - name === 'dhwani โ€“ devops release & qc board' || // Different dash character + name === 'dhwani โ€“ devops release & qc board' || // en-dash (U+2013) + name === 'dhwani โ€” devops release & qc board' || // em-dash (U+2014) + name === 'dhwani - devops release and qc board' || + name === 'dhwani โ€“ devops release and qc board' || (name.includes('dhwani') && name.includes('devops') && name.includes('release') && name.includes('qc')) || + (name.includes('dhwani') && name.includes('devops') && name.includes('release') && name.includes('board')) || (name.includes('devops') && name.includes('release') && name.includes('qc')) || + (name.includes('devops') && name.includes('release') && name.includes('board')) || (name.includes('devops') && name.includes('release')) || (name.includes('devops') && name.includes('qc')); }); @@ -67,8 +74,13 @@ jobs: if (!devopsProject) { console.log('โŒ DevOps Release & QC Board project not found'); - console.log('Available project names:', projects.map(p => p.name).join(', ')); - console.log('Please check the project name matches: "DevOps", "Release", or "QC"'); + console.log('Available project names:', projects.map(p => `"${p.name}"`).join(', ')); + console.log('Searched for: "Dhwani โ€“ DevOps Release & QC Board" (with en-dash)'); + console.log('Please check:'); + console.log(' 1. Project name matches: "DevOps", "Release", or "QC"'); + console.log(' 2. Project is in the dhwani-ris organization'); + console.log(' 3. GITHUB_TOKEN has organization project access'); + console.log(' 4. Consider using PROJECT_TOKEN secret with "project" scope'); return; } @@ -90,31 +102,34 @@ jobs: const firstColumn = columns[0]; console.log(`Adding PR to column: "${firstColumn.name}"`); - // Check if PR is already in the project - const { data: cards } = await github.rest.projects.listCards({ - column_id: firstColumn.id - }); - - const alreadyAdded = cards.some(card => - card.content_url && card.content_url.includes(`/pulls/${prNumber}`) - ); - - if (alreadyAdded) { - console.log(`โœ… PR #${prNumber} is already in the project`); - return; + // Check if PR is already in the project (check all columns) + let alreadyAdded = false; + for (const column of columns) { + const { data: cards } = await github.rest.projects.listCards({ + column_id: column.id + }); + alreadyAdded = cards.some(card => + card.content_url && card.content_url.includes(`/pulls/${prNumber}`) + ); + if (alreadyAdded) { + console.log(`โœ… PR #${prNumber} is already in the project (column: ${column.name})`); + break; + } } - // Add PR to the first column - await github.rest.projects.createCard({ - column_id: firstColumn.id, - content_id: prNodeId, - content_type: 'PullRequest' - }); - - console.log(`โœ… Successfully added PR #${prNumber} to DevOps Release & QC Board`); - console.log(` Project: "${devopsProject.name}"`); - console.log(` Column: "${firstColumn.name}"`); - console.log(` PR URL: ${prUrl}`); + if (!alreadyAdded) { + // Add PR to the first column + await github.rest.projects.createCard({ + column_id: firstColumn.id, + content_id: prNodeId, + content_type: 'PullRequest' + }); + + console.log(`โœ… Successfully added PR #${prNumber} to DevOps Release & QC Board`); + console.log(` Project: "${devopsProject.name}"`); + console.log(` Column: "${firstColumn.name}"`); + console.log(` PR URL: ${prUrl}`); + } } catch (error) { console.log('โŒ Error adding PR to project:', error.message); console.log('Error details:', JSON.stringify(error, null, 2)); diff --git a/.github/workflows/auto-reviewer.yml b/.github/workflows/auto-reviewer.yml index f97e436..bc383a5 100644 --- a/.github/workflows/auto-reviewer.yml +++ b/.github/workflows/auto-reviewer.yml @@ -3,7 +3,7 @@ name: Auto Request Review on: pull_request: types: [opened, synchronize, reopened, ready_for_review, closed] - branches: [master] + branches: [main, master, develop, development] permissions: pull-requests: write @@ -47,27 +47,46 @@ jobs: console.log('Current reviewers:', JSON.stringify(currentReviews, null, 2)); - // Check if reviewer is already requested - const isAlreadyRequested = currentReviews.users?.some( + // Check if DevOps team is already requested + const devopsTeam = 'dhwani-ris/dhwani-devops'; + const isTeamRequested = currentReviews.teams?.some( + team => `${team.organization.login}/${team.slug}` === devopsTeam + ); + + // Check if individual reviewer is already requested + const isUserRequested = currentReviews.users?.some( user => user.login.toLowerCase() === reviewer.toLowerCase() - ) || currentReviews.teams?.some( - team => team.slug.toLowerCase() === reviewer.toLowerCase() ); - if (isAlreadyRequested) { - console.log(`โœ… Review already requested from ${reviewer}`); - return; + // Request review from DevOps team (from CODEOWNERS) + if (!isTeamRequested) { + try { + await github.rest.pulls.requestReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number, + team_reviewers: ['dhwani-devops'], + }); + console.log(`โœ… Successfully requested review from ${devopsTeam}`); + } catch (teamError) { + console.log(`โš ๏ธ Could not request review from team ${devopsTeam}:`, teamError.message); + } + } else { + console.log(`โœ… Review already requested from ${devopsTeam}`); } // Request review from default reviewer - await github.rest.pulls.requestReviewers({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: pr.number, - reviewers: [reviewer], - }); - - console.log(`โœ… Successfully requested review from ${reviewer}`); + if (!isUserRequested) { + await github.rest.pulls.requestReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number, + reviewers: [reviewer], + }); + console.log(`โœ… Successfully requested review from ${reviewer}`); + } else { + console.log(`โœ… Review already requested from ${reviewer}`); + } } catch (error) { console.error(`โŒ Error requesting review from ${reviewer}:`, error); console.error('Error details:', JSON.stringify(error, null, 2)); From bc576af3fb96a999087eefe5a99757a64f2d8728 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Mon, 1 Dec 2025 11:38:02 +0530 Subject: [PATCH 17/21] fix: fixing adding project and review thing --- .github/workflows/add-pr-to-devops.yml | 216 ++++++++++++++++++------- .github/workflows/auto-reviewer.yml | 17 +- 2 files changed, 175 insertions(+), 58 deletions(-) diff --git a/.github/workflows/add-pr-to-devops.yml b/.github/workflows/add-pr-to-devops.yml index dc3608f..03250bd 100644 --- a/.github/workflows/add-pr-to-devops.yml +++ b/.github/workflows/add-pr-to-devops.yml @@ -18,11 +18,12 @@ jobs: pull-requests: write repository-projects: write organization-projects: write + issues: read steps: - name: Add PR to DevOps Release Board uses: actions/github-script@v8 with: - github-token: ${{ secrets.PROJECT_TOKEN || secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.GITHUB_TOKEN }} script: | const prNodeId = context.payload.pull_request.node_id; const prNumber = context.payload.pull_request.number; @@ -31,11 +32,44 @@ jobs: console.log(`Processing PR #${prNumber} (Node ID: ${prNodeId})`); try { - // Get organization projects - const { data: projects } = await github.rest.projects.listForOrg({ - org: 'dhwani-ris', - state: 'open' - }); + // Try GraphQL API first (more reliable for projects) + let projects = []; + try { + const graphqlQuery = ` + query { + organization(login: "dhwani-ris") { + projectsV2(first: 50, states: OPEN) { + nodes { + id + title + number + } + } + } + } + `; + const graphqlResponse = await github.graphql(graphqlQuery); + if (graphqlResponse?.organization?.projectsV2?.nodes) { + projects = graphqlResponse.organization.projectsV2.nodes.map(p => ({ + id: p.id, + name: p.title, + number: p.number + })); + console.log(`Found ${projects.length} projects via GraphQL`); + } + } catch (graphqlError) { + console.log('GraphQL query failed, falling back to REST API:', graphqlError.message); + } + + // Fallback to REST API if GraphQL didn't work + if (projects.length === 0) { + const { data: restProjects } = await github.rest.projects.listForOrg({ + org: 'dhwani-ris', + state: 'open' + }); + projects = restProjects; + console.log(`Found ${projects.length} projects via REST API`); + } console.log(`Found ${projects.length} organization projects`); @@ -76,74 +110,144 @@ jobs: console.log('โŒ DevOps Release & QC Board project not found'); console.log('Available project names:', projects.map(p => `"${p.name}"`).join(', ')); console.log('Searched for: "Dhwani โ€“ DevOps Release & QC Board" (with en-dash)'); - console.log('Please check:'); - console.log(' 1. Project name matches: "DevOps", "Release", or "QC"'); - console.log(' 2. Project is in the dhwani-ris organization'); - console.log(' 3. GITHUB_TOKEN has organization project access'); - console.log(' 4. Consider using PROJECT_TOKEN secret with "project" scope'); return; } console.log(`โœ… Found project: "${devopsProject.name}" (ID: ${devopsProject.id})`); - // Get project columns - const { data: columns } = await github.rest.projects.listColumns({ - project_id: devopsProject.id - }); - - if (columns.length === 0) { - console.log('โŒ No columns found in project'); - return; - } + // Check if this is a Projects V2 (new) or Projects V1 (old) + const isV2Project = devopsProject.id && typeof devopsProject.id === 'string' && devopsProject.id.startsWith('PVT_'); - console.log(`Found ${columns.length} columns:`, columns.map(c => `"${c.name}"`).join(', ')); - - // Find the first column (usually "To do", "In progress", or "Backlog") - const firstColumn = columns[0]; - console.log(`Adding PR to column: "${firstColumn.name}"`); - - // Check if PR is already in the project (check all columns) - let alreadyAdded = false; - for (const column of columns) { - const { data: cards } = await github.rest.projects.listCards({ - column_id: column.id + if (isV2Project) { + // Use GraphQL for Projects V2 + console.log('Using Projects V2 (GraphQL)'); + + // Get project fields/columns + const projectQuery = ` + query($projectId: ID!) { + node(id: $projectId) { + ... on ProjectV2 { + id + title + fields(first: 20) { + nodes { + ... on ProjectV2Field { + id + name + } + ... on ProjectV2SingleSelectField { + id + name + options { + id + name + } + } + } + } + views(first: 10) { + nodes { + id + name + } + } + } + } + } + `; + + const projectData = await github.graphql(projectQuery, { + projectId: devopsProject.id }); - alreadyAdded = cards.some(card => - card.content_url && card.content_url.includes(`/pulls/${prNumber}`) - ); - if (alreadyAdded) { - console.log(`โœ… PR #${prNumber} is already in the project (column: ${column.name})`); - break; + + // Add item to project using GraphQL + const addItemMutation = ` + mutation($projectId: ID!, $contentId: ID!) { + addProjectV2ItemById(input: { + projectId: $projectId + contentId: $contentId + }) { + item { + id + } + } + } + `; + + try { + await github.graphql(addItemMutation, { + projectId: devopsProject.id, + contentId: prNodeId + }); + console.log(`โœ… Successfully added PR #${prNumber} to DevOps Release & QC Board (Projects V2)`); + console.log(` Project: "${devopsProject.name}"`); + console.log(` PR URL: ${prUrl}`); + } catch (addError) { + if (addError.message && addError.message.includes('already exists')) { + console.log(`โœ… PR #${prNumber} is already in the project`); + } else { + throw addError; + } } - } - - if (!alreadyAdded) { - // Add PR to the first column - await github.rest.projects.createCard({ - column_id: firstColumn.id, - content_id: prNodeId, - content_type: 'PullRequest' + } else { + // Use REST API for Projects V1 + console.log('Using Projects V1 (REST API)'); + + // Get project columns + const { data: columns } = await github.rest.projects.listColumns({ + project_id: devopsProject.id }); - console.log(`โœ… Successfully added PR #${prNumber} to DevOps Release & QC Board`); - console.log(` Project: "${devopsProject.name}"`); - console.log(` Column: "${firstColumn.name}"`); - console.log(` PR URL: ${prUrl}`); + if (columns.length === 0) { + console.log('โŒ No columns found in project'); + return; + } + + console.log(`Found ${columns.length} columns:`, columns.map(c => `"${c.name}"`).join(', ')); + + // Find the first column (usually "To do", "In progress", or "Backlog") + const firstColumn = columns[0]; + console.log(`Adding PR to column: "${firstColumn.name}"`); + + // Check if PR is already in the project (check all columns) + let alreadyAdded = false; + for (const column of columns) { + const { data: cards } = await github.rest.projects.listCards({ + column_id: column.id + }); + alreadyAdded = cards.some(card => + card.content_url && card.content_url.includes(`/pulls/${prNumber}`) + ); + if (alreadyAdded) { + console.log(`โœ… PR #${prNumber} is already in the project (column: ${column.name})`); + break; + } + } + + if (!alreadyAdded) { + // Add PR to the first column + await github.rest.projects.createCard({ + column_id: firstColumn.id, + content_id: prNodeId, + content_type: 'PullRequest' + }); + + console.log(`โœ… Successfully added PR #${prNumber} to DevOps Release & QC Board`); + console.log(` Project: "${devopsProject.name}"`); + console.log(` Column: "${firstColumn.name}"`); + console.log(` PR URL: ${prUrl}`); + } } } catch (error) { console.log('โŒ Error adding PR to project:', error.message); console.log('Error details:', JSON.stringify(error, null, 2)); if (error.status === 403) { - console.log('โš ๏ธ Permission denied (403). GITHUB_TOKEN may not have organization project access.'); - console.log(' Solution: Use a Personal Access Token (PAT) with "project" scope.'); - console.log(' 1. Create PAT at: https://github.com/settings/tokens'); - console.log(' 2. Add it as secret: PROJECT_TOKEN'); - console.log(' 3. Update workflow to use: ${{ secrets.PROJECT_TOKEN }}'); + console.log('โš ๏ธ Permission denied (403). Check repository permissions.'); } else if (error.status === 404) { - console.log('โš ๏ธ Not found (404). Check project name and permissions.'); + console.log('โš ๏ธ Not found (404). Check project name.'); } else if (error.status === 401) { - console.log('โš ๏ธ Unauthorized (401). Token may be invalid or expired.'); + console.log('โš ๏ธ Unauthorized (401). Check authentication.'); } // Don't fail the workflow, just log the error diff --git a/.github/workflows/auto-reviewer.yml b/.github/workflows/auto-reviewer.yml index bc383a5..b53d3f4 100644 --- a/.github/workflows/auto-reviewer.yml +++ b/.github/workflows/auto-reviewer.yml @@ -14,8 +14,7 @@ jobs: name: Request Review from Default Reviewer runs-on: ubuntu-latest if: | - (github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'synchronize' || github.event.action == 'ready_for_review') && - (github.event.pull_request.base.ref == 'main' || github.event.pull_request.base.ref == 'master') + (github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'synchronize' || github.event.action == 'ready_for_review') steps: - name: Request review from default reviewer @@ -61,6 +60,7 @@ jobs: // Request review from DevOps team (from CODEOWNERS) if (!isTeamRequested) { try { + // Try with just team slug first await github.rest.pulls.requestReviewers({ owner: context.repo.owner, repo: context.repo.repo, @@ -70,6 +70,19 @@ jobs: console.log(`โœ… Successfully requested review from ${devopsTeam}`); } catch (teamError) { console.log(`โš ๏ธ Could not request review from team ${devopsTeam}:`, teamError.message); + console.log(` Error details:`, JSON.stringify(teamError, null, 2)); + // Try alternative: get team ID and use GraphQL + try { + const { data: teams } = await github.rest.teams.list({ + org: 'dhwani-ris', + }); + const devopsTeamData = teams.find(t => t.slug === 'dhwani-devops'); + if (devopsTeamData) { + console.log(` Found team ID: ${devopsTeamData.id}, trying alternative method...`); + } + } catch (e) { + console.log(` Could not fetch team info:`, e.message); + } } } else { console.log(`โœ… Review already requested from ${devopsTeam}`); From 7b67ddc169cfe98b176cdf36a6752e534021659e Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Mon, 1 Dec 2025 11:43:37 +0530 Subject: [PATCH 18/21] fix: fixing adding project and review thing --- .github/workflows/add-pr-to-devops.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/add-pr-to-devops.yml b/.github/workflows/add-pr-to-devops.yml index 03250bd..1c11db5 100644 --- a/.github/workflows/add-pr-to-devops.yml +++ b/.github/workflows/add-pr-to-devops.yml @@ -22,6 +22,8 @@ jobs: steps: - name: Add PR to DevOps Release Board uses: actions/github-script@v8 + # Note: GITHUB_TOKEN is automatically provided by GitHub Actions + # No need to add it as a secret - it's available in all workflows with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | From cb0f5ae671ca30be4d0fa14d2d9ef551b860faa9 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Mon, 1 Dec 2025 11:53:30 +0530 Subject: [PATCH 19/21] fix: fixing adding project and review thing --- .github/workflows/add-pr-to-devops.yml | 70 +++++++++++++++++++------- .github/workflows/auto-reviewer.yml | 53 +++++++++++++------ 2 files changed, 90 insertions(+), 33 deletions(-) diff --git a/.github/workflows/add-pr-to-devops.yml b/.github/workflows/add-pr-to-devops.yml index 1c11db5..325ba3f 100644 --- a/.github/workflows/add-pr-to-devops.yml +++ b/.github/workflows/add-pr-to-devops.yml @@ -2,7 +2,7 @@ name: Add PR to DevOps Board on: pull_request: - types: [opened, reopened, synchronize] + types: [opened, reopened, synchronize, ready_for_review] branches: [main, master, develop, development] jobs: @@ -85,27 +85,63 @@ jobs: // Find the DevOps Release & QC Board project - try exact match first // Project name from UI: "Dhwani โ€“ DevOps Release & QC Board" (with en-dash) - let devopsProject = projects.find(p => { - const name = p.name.toLowerCase(); - // Try exact match with various dash characters (hyphen, en-dash, em-dash) - return name === 'dhwani - devops release & qc board' || - name === 'dhwani โ€“ devops release & qc board' || // en-dash (U+2013) - name === 'dhwani โ€” devops release & qc board' || // em-dash (U+2014) - name === 'dhwani - devops release and qc board' || - name === 'dhwani โ€“ devops release and qc board' || - (name.includes('dhwani') && name.includes('devops') && name.includes('release') && name.includes('qc')) || - (name.includes('dhwani') && name.includes('devops') && name.includes('release') && name.includes('board')) || - (name.includes('devops') && name.includes('release') && name.includes('qc')) || - (name.includes('devops') && name.includes('release') && name.includes('board')) || - (name.includes('devops') && name.includes('release')) || - (name.includes('devops') && name.includes('qc')); - }); + let devopsProject = null; - // If not found, try just "devops" + // Try multiple matching strategies + const searchTerms = [ + 'dhwani - devops release & qc board', + 'dhwani โ€“ devops release & qc board', // en-dash (U+2013) + 'dhwani โ€” devops release & qc board', // em-dash (U+2014) + 'dhwani - devops release and qc board', + 'dhwani โ€“ devops release and qc board', + 'dhwani devops release qc board', + 'devops release qc board' + ]; + + for (const term of searchTerms) { + devopsProject = projects.find(p => + p.name.toLowerCase().trim() === term + ); + if (devopsProject) { + console.log(`โœ… Found exact match: "${devopsProject.name}"`); + break; + } + } + + // If no exact match, try partial matches + if (!devopsProject) { + devopsProject = projects.find(p => { + const name = p.name.toLowerCase(); + return (name.includes('dhwani') && name.includes('devops') && name.includes('release') && name.includes('qc')) || + (name.includes('dhwani') && name.includes('devops') && name.includes('release') && name.includes('board')) || + (name.includes('devops') && name.includes('release') && name.includes('qc')) || + (name.includes('devops') && name.includes('release') && name.includes('board')); + }); + if (devopsProject) { + console.log(`โœ… Found partial match: "${devopsProject.name}"`); + } + } + + // If still not found, try just "devops" and "release" + if (!devopsProject) { + devopsProject = projects.find(p => { + const name = p.name.toLowerCase(); + return (name.includes('devops') && name.includes('release')) || + (name.includes('devops') && name.includes('qc')); + }); + if (devopsProject) { + console.log(`โœ… Found loose match: "${devopsProject.name}"`); + } + } + + // Last resort: any project with "devops" if (!devopsProject) { devopsProject = projects.find(p => p.name.toLowerCase().includes('devops') ); + if (devopsProject) { + console.log(`โœ… Found devops project: "${devopsProject.name}"`); + } } if (!devopsProject) { diff --git a/.github/workflows/auto-reviewer.yml b/.github/workflows/auto-reviewer.yml index b53d3f4..7eea6e4 100644 --- a/.github/workflows/auto-reviewer.yml +++ b/.github/workflows/auto-reviewer.yml @@ -60,28 +60,49 @@ jobs: // Request review from DevOps team (from CODEOWNERS) if (!isTeamRequested) { try { - // Try with just team slug first - await github.rest.pulls.requestReviewers({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: pr.number, - team_reviewers: ['dhwani-devops'], + // First, verify team exists + const { data: teams } = await github.rest.teams.list({ + org: 'dhwani-ris', }); - console.log(`โœ… Successfully requested review from ${devopsTeam}`); + console.log(`Available teams:`, teams.map(t => t.slug).join(', ')); + + const devopsTeamData = teams.find(t => + t.slug === 'dhwani-devops' || + t.slug === 'devops' || + t.name.toLowerCase().includes('devops') + ); + + if (!devopsTeamData) { + console.log(`โš ๏ธ Team "dhwani-devops" not found in organization`); + console.log(` Available teams:`, teams.map(t => `${t.slug} (${t.name})`).join(', ')); + } else { + console.log(`โœ… Found team: ${devopsTeamData.slug} (ID: ${devopsTeamData.id})`); + + // Request review using team slug + await github.rest.pulls.requestReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number, + team_reviewers: [devopsTeamData.slug], + }); + console.log(`โœ… Successfully requested review from ${devopsTeam}`); + } } catch (teamError) { console.log(`โš ๏ธ Could not request review from team ${devopsTeam}:`, teamError.message); + console.log(` Error status:`, teamError.status); console.log(` Error details:`, JSON.stringify(teamError, null, 2)); - // Try alternative: get team ID and use GraphQL + + // Try with just the slug as fallback try { - const { data: teams } = await github.rest.teams.list({ - org: 'dhwani-ris', + await github.rest.pulls.requestReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number, + team_reviewers: ['dhwani-devops'], }); - const devopsTeamData = teams.find(t => t.slug === 'dhwani-devops'); - if (devopsTeamData) { - console.log(` Found team ID: ${devopsTeamData.id}, trying alternative method...`); - } - } catch (e) { - console.log(` Could not fetch team info:`, e.message); + console.log(`โœ… Successfully requested review using fallback method`); + } catch (fallbackError) { + console.log(`โŒ Fallback also failed:`, fallbackError.message); } } } else { From 43e78703652495e40b9a2ea8e643803c7ba7cd5f Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Mon, 1 Dec 2025 12:04:20 +0530 Subject: [PATCH 20/21] feat: adding unit test case workflow --- .github/workflows/ci.yml | 145 +------------ .github/workflows/frappe-bench-tests.yml | 261 +++++++++++++++++++++++ 2 files changed, 263 insertions(+), 143 deletions(-) create mode 100644 .github/workflows/frappe-bench-tests.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12775b6..d61123a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,149 +101,8 @@ jobs: run: | echo "Codecov upload completed. Check https://codecov.io/gh/${{ github.repository }} for coverage reports." - frappe-bench-test: - name: 'Frappe Bench App Tests' - runs-on: ubuntu-latest - if: hashFiles('**/__init__.py') != '' || hashFiles('**/hooks.py') != '' - - services: - mariadb: - image: mariadb:10.11 - env: - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: test_frappe - ports: - - 3306:3306 - options: >- - --health-cmd="mysqladmin ping" - --health-interval=10s - --health-timeout=5s - --health-retries=3 - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - cache: pip - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 18 - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - mariadb-client \ - redis-server \ - curl \ - git \ - wget \ - xvfb \ - libfontconfig1 \ - libfreetype6 \ - libxrender1 \ - libjpeg-turbo8 \ - xfonts-75dpi \ - xfonts-base - - - name: Install Frappe Bench CLI - run: | - pip install frappe-bench - - - name: Initialize Frappe Bench - run: | - bench init --skip-redis-config-check --skip-assets --frappe-branch version-15 frappe-bench - cd frappe-bench - - - name: Detect app name - id: app-name - working-directory: frappe-bench - run: | - # Try to detect app name from current directory structure - if [ -d "../apps" ]; then - APP_NAME=$(ls -d ../apps/*/ 2>/dev/null | head -1 | xargs basename) - elif [ -d "apps" ]; then - APP_NAME=$(ls -d apps/*/ 2>/dev/null | head -1 | xargs basename) - else - # Check if current repo is an app - if [ -f "../__init__.py" ] || [ -f "../hooks.py" ]; then - APP_NAME=$(basename $(cd .. && pwd)) - else - # Default: use repo name - APP_NAME=$(echo "${{ github.repository }}" | cut -d'/' -f2) - fi - fi - - if [ -z "$APP_NAME" ] || [ "$APP_NAME" = "." ]; then - APP_NAME="gatepass_check" - fi - - echo "name=$APP_NAME" >> $GITHUB_OUTPUT - echo "Detected app name: $APP_NAME" - - - name: Get app into bench - working-directory: frappe-bench - run: | - APP_NAME="${{ steps.app-name.outputs.name }}" - # If app is in parent directory, create symlink or copy - if [ -d "../$APP_NAME" ] && [ -f "../$APP_NAME/__init__.py" ]; then - bench get-app $APP_NAME ../$APP_NAME - elif [ -d "../apps/$APP_NAME" ]; then - bench get-app $APP_NAME ../apps/$APP_NAME - else - # Clone or use current repo as app - bench get-app $APP_NAME ${{ github.repository_url }} || \ - bench get-app $APP_NAME file://$(cd .. && pwd) || \ - echo "App $APP_NAME not found, skipping app-specific tests" - fi - - - name: Create test site - working-directory: frappe-bench - env: - DB_HOST: 127.0.0.1 - DB_PORT: 3306 - DB_ROOT_USER: root - DB_ROOT_PASSWORD: root - run: | - bench new-site test_site \ - --db-type mariadb \ - --admin-password admin \ - --no-mariadb-socket \ - --mariadb-host 127.0.0.1 \ - --mariadb-port 3306 \ - --mariadb-root-password root \ - --install-app ${{ steps.app-name.outputs.name }} || \ - echo "Site creation or app installation failed, will try to run tests anyway" - - - name: Run app-specific tests - working-directory: frappe-bench - env: - DB_HOST: 127.0.0.1 - DB_PORT: 3306 - run: | - APP_NAME="${{ steps.app-name.outputs.name }}" - echo "Running tests for app: $APP_NAME" - - # Try to run app-specific tests - bench --site test_site run-tests --app $APP_NAME || \ - bench --site test_site run-tests --app $APP_NAME --coverage || \ - echo "No tests found for app $APP_NAME or test execution failed" - continue-on-error: true - - - name: Upload test results - if: always() - uses: actions/upload-artifact@v4 - with: - name: frappe-test-results - path: | - frappe-bench/sites/test_site/logs/*.log - if-no-files-found: ignore + # Frappe Bench tests are now in a separate workflow: frappe-bench-tests.yml + # This keeps the CI workflow focused and allows bench tests to be optional security: name: 'Security Check' diff --git a/.github/workflows/frappe-bench-tests.yml b/.github/workflows/frappe-bench-tests.yml new file mode 100644 index 0000000..f2c8929 --- /dev/null +++ b/.github/workflows/frappe-bench-tests.yml @@ -0,0 +1,261 @@ +name: Frappe Bench Unit Tests + +on: + pull_request: + branches: [ main, develop, development, master ] + push: + branches: [ main, develop, development, master ] + workflow_dispatch: + +permissions: + contents: read + +# This workflow is optional and won't block merges +concurrency: + group: frappe-bench-tests-${{ github.event_name }}-${{ github.ref }}-${{ github.event.number || github.sha }} + cancel-in-progress: false + +jobs: + frappe-bench-test: + name: 'Frappe Bench App Tests (Optional)' + runs-on: ubuntu-latest + # This job is optional and won't block merges + continue-on-error: true + if: hashFiles('**/__init__.py') != '' || hashFiles('**/hooks.py') != '' + + services: + mariadb: + image: mariadb:10.11 + env: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: test_frappe + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: pip + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 18 + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + mariadb-client \ + redis-server \ + curl \ + git \ + wget \ + xvfb \ + libfontconfig1 \ + libfreetype6 \ + libxrender1 \ + libjpeg-turbo8 \ + xfonts-75dpi \ + xfonts-base + + - name: Install Frappe Bench CLI + run: | + pip install frappe-bench + + - name: Initialize Frappe Bench + continue-on-error: true + run: | + echo "๐Ÿš€ Initializing Frappe Bench environment..." + bench init --skip-redis-config-check --skip-assets --frappe-branch version-15 frappe-bench || { + echo "โš ๏ธ Bench initialization failed, but continuing..." + exit 0 + } + cd frappe-bench + echo "โœ… Bench initialized successfully" + + - name: Detect app name + id: app-name + working-directory: frappe-bench + run: | + # Try to detect app name from current directory structure + if [ -d "../apps" ]; then + APP_NAME=$(ls -d ../apps/*/ 2>/dev/null | head -1 | xargs basename) + elif [ -d "apps" ]; then + APP_NAME=$(ls -d apps/*/ 2>/dev/null | head -1 | xargs basename) + else + # Check if current repo is an app + if [ -f "../__init__.py" ] || [ -f "../hooks.py" ]; then + APP_NAME=$(basename $(cd .. && pwd)) + else + # Default: use repo name + APP_NAME=$(echo "${{ github.repository }}" | cut -d'/' -f2) + fi + fi + + if [ -z "$APP_NAME" ] || [ "$APP_NAME" = "." ]; then + APP_NAME="gatepass_check" + fi + + echo "name=$APP_NAME" >> $GITHUB_OUTPUT + echo "Detected app name: $APP_NAME" + + - name: Get app into bench + working-directory: frappe-bench + continue-on-error: true + run: | + APP_NAME="${{ steps.app-name.outputs.name }}" + echo "๐Ÿ“ฆ Installing app: $APP_NAME into bench..." + + # If app is in parent directory, create symlink or copy + if [ -d "../$APP_NAME" ] && [ -f "../$APP_NAME/__init__.py" ]; then + bench get-app $APP_NAME ../$APP_NAME || echo "โš ๏ธ Failed to get app from ../$APP_NAME" + elif [ -d "../apps/$APP_NAME" ]; then + bench get-app $APP_NAME ../apps/$APP_NAME || echo "โš ๏ธ Failed to get app from ../apps/$APP_NAME" + else + # Clone or use current repo as app + bench get-app $APP_NAME ${{ github.repository_url }} || \ + bench get-app $APP_NAME file://$(cd .. && pwd) || \ + echo "โš ๏ธ App $APP_NAME not found, will try to continue anyway" + fi + + # Verify app is installed + if bench list-apps | grep -q "$APP_NAME"; then + echo "โœ… App $APP_NAME successfully installed in bench" + else + echo "โš ๏ธ App $APP_NAME not found in bench, but continuing..." + fi + + - name: Create test site + working-directory: frappe-bench + continue-on-error: true + env: + DB_HOST: 127.0.0.1 + DB_PORT: 3306 + DB_ROOT_USER: root + DB_ROOT_PASSWORD: root + run: | + APP_NAME="${{ steps.app-name.outputs.name }}" + echo "๐Ÿ—๏ธ Creating test site with app: $APP_NAME..." + + # Wait for MariaDB to be ready + echo "Waiting for MariaDB to be ready..." + for i in {1..30}; do + if mysql -h 127.0.0.1 -P 3306 -u root -proot -e "SELECT 1" &>/dev/null; then + echo "โœ… MariaDB is ready" + break + fi + echo "Waiting for MariaDB... ($i/30)" + sleep 2 + done + + # Create site + bench new-site test_site \ + --db-type mariadb \ + --admin-password admin \ + --no-mariadb-socket \ + --mariadb-host 127.0.0.1 \ + --mariadb-port 3306 \ + --mariadb-root-password root || { + echo "โš ๏ธ Site creation failed, but continuing..." + exit 0 + } + + # Install app if site was created + if bench --site test_site list-apps &>/dev/null; then + echo "๐Ÿ“ฆ Installing app $APP_NAME in test site..." + bench --site test_site install-app $APP_NAME || { + echo "โš ๏ธ App installation failed, but continuing..." + } + echo "โœ… Test site created and app installed" + else + echo "โš ๏ธ Site creation may have failed, but continuing..." + fi + + - name: Run app-specific tests + working-directory: frappe-bench + continue-on-error: true + env: + DB_HOST: 127.0.0.1 + DB_PORT: 3306 + run: | + APP_NAME="${{ steps.app-name.outputs.name }}" + echo "๐Ÿงช Running unit tests for app: $APP_NAME" + + # Check if site exists + if ! bench --site test_site list-apps &>/dev/null; then + echo "โš ๏ธ Test site not found, skipping tests" + exit 0 + fi + + # Check if app is installed + if ! bench --site test_site list-apps | grep -q "$APP_NAME"; then + echo "โš ๏ธ App $APP_NAME not installed in test site, skipping tests" + exit 0 + fi + + # Try to run app-specific tests + echo "Running tests with bench run-tests..." + if bench --site test_site run-tests --app $APP_NAME; then + echo "โœ… Tests passed for app $APP_NAME" + elif bench --site test_site run-tests --app $APP_NAME --coverage; then + echo "โœ… Tests passed with coverage for app $APP_NAME" + else + echo "โš ๏ธ No tests found for app $APP_NAME or test execution failed" + echo "This is optional and won't block the merge" + exit 0 + fi + + - name: Upload test results and logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: frappe-bench-test-results + path: | + frappe-bench/sites/test_site/logs/*.log + frappe-bench/sites/test_site/logs/*.txt + if-no-files-found: ignore + retention-days: 7 + + - name: Test Summary + if: always() + run: | + echo "## ๐Ÿ“Š Frappe Bench Test Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Note:** This test job is **optional** and will not block merges." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ -d "frappe-bench/sites/test_site" ]; then + echo "โœ… Test site was created" >> $GITHUB_STEP_SUMMARY + else + echo "โš ๏ธ Test site was not created" >> $GITHUB_STEP_SUMMARY + fi + + APP_NAME="${{ steps.app-name.outputs.name }}" + if [ -d "frappe-bench/apps/$APP_NAME" ]; then + echo "โœ… App $APP_NAME was installed in bench" >> $GITHUB_STEP_SUMMARY + else + echo "โš ๏ธ App $APP_NAME was not found in bench" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "Check the workflow logs for detailed test results." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Test Environment" >> $GITHUB_STEP_SUMMARY + echo "- **Frappe Version:** version-15" >> $GITHUB_STEP_SUMMARY + echo "- **Python Version:** 3.11" >> $GITHUB_STEP_SUMMARY + echo "- **Node.js Version:** 18" >> $GITHUB_STEP_SUMMARY + echo "- **Database:** MariaDB 10.11" >> $GITHUB_STEP_SUMMARY + From 13e2d99e0ddd5888510def0f7b178384382f8ed5 Mon Sep 17 00:00:00 2001 From: ankitjangir1690 Date: Mon, 1 Dec 2025 12:24:31 +0530 Subject: [PATCH 21/21] fix: unit test case workflow --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d61123a..7cc0890 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,9 +30,10 @@ jobs: - name: Cache pip uses: actions/cache@v4 + continue-on-error: true with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') || 'no-deps' }} restore-keys: | ${{ runner.os }}-pip- ${{ runner.os }}- @@ -58,7 +59,8 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - cache: pip + # Cache disabled - no requirements.txt or pyproject.toml found + # cache: pip - name: Install dependencies run: |