diff --git a/.github/changes-filter.yaml b/.github/changes-filter.yaml new file mode 100644 index 00000000..e6b8c971 --- /dev/null +++ b/.github/changes-filter.yaml @@ -0,0 +1,51 @@ +# https://github.com/dorny/paths-filter +python: + - "src/**" + - "pyproject.toml" + - "uv.lock" + - "**/test-integration.yml" + +frontend: + - "frontend/**" + - "frontend/package.json" + - "frontend/package-lock.json" + +docs: + - "docs/**" + +docker: + - "docker-compose*.yml" + - "Dockerfile*" + - "uv.lock" + - "pyproject.toml" + - "src/**" + - "frontend/**" + - ".dockerignore" + +tests: + - "tests/**" + - "src/**" + +api: + - "src/api/**" + - "src/main.py" + +services: + - "src/services/**" + +connectors: + - "src/connectors/**" + +flows: + - "flows/**" + +config: + - "config/**" + - "securityconfig/**" + +sdks: + - "sdks/**" + +scripts: + - "scripts/**" + diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..a30df01a --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,26 @@ +frontend: + - changed-files: + - any-glob-to-any-file: 'frontend/**' + +backend: + - changed-files: + - any-glob-to-any-file: 'src/**' + +documentation: + - changed-files: + - any-glob-to-any-file: 'docs/**' + +ci: + - changed-files: + - any-glob-to-any-file: '.github/**' + +tests: + - changed-files: + - any-glob-to-any-file: 'tests/**' + +docker: + - changed-files: + - any-glob-to-any-file: + - 'Dockerfile*' + - 'docker-compose*.yml' + diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 00000000..674f872d --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,36 @@ +changelog: + categories: + - title: 🚨 Breaking Changes + description: Changes that break existing functionality + labels: + - breaking + - title: ✨ New Features + description: New features and enhancements + labels: + - enhancement + - title: 🐛 Bug Fixes + description: Bug fixes and patches + labels: + - fix + - bug + - title: 📝 Documentation Updates + description: Changes to documentation + labels: + - documentation + - title: 🛠 Maintenance Tasks + description: Maintenance tasks and housekeeping + labels: + - chore + - refactor + - style + - performance + - build + - title: ✅ Tests + description: Changes to tests + labels: + - test + - title: Others + description: Other changes + labels: + - "*" + diff --git a/.github/semantic.yml b/.github/semantic.yml new file mode 100644 index 00000000..63e14e9c --- /dev/null +++ b/.github/semantic.yml @@ -0,0 +1,3 @@ +titleOnly: true +targetUrl: https://www.conventionalcommits.org/en/v1.0.0/#summary + diff --git a/.github/workflows/add-labels.yml b/.github/workflows/add-labels.yml new file mode 100644 index 00000000..0ac38b83 --- /dev/null +++ b/.github/workflows/add-labels.yml @@ -0,0 +1,66 @@ +name: Manage Review Labels + +on: + pull_request_review: + types: [submitted] + +jobs: + label-on-review: + runs-on: ubuntu-latest + permissions: + pull-requests: write + issues: write + steps: + - name: Manage LGTM Review Label + uses: actions/github-script@v8.0 + with: + script: | + const LGTM_LABEL = 'lgtm'; + + // Extract review details + const { state: reviewState } = context.payload.review; + const pullRequestNumber = context.payload.pull_request.number; + const repoDetails = { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pullRequestNumber + }; + + // Log review information + console.log(`Processing review for PR #${pullRequestNumber}`); + console.log(`Review state: ${reviewState}`); + + // Helper function to check for LGTM label + async function hasLgtmLabel() { + const { data: labels } = await github.rest.issues.listLabelsOnIssue(repoDetails); + return labels.some(label => label.name === LGTM_LABEL); + } + + if (reviewState === 'approved') { + const lgtmExists = await hasLgtmLabel(); + + if (!lgtmExists) { + console.log(`Adding ${LGTM_LABEL} label to PR #${pullRequestNumber}`); + await github.rest.issues.addLabels({ + ...repoDetails, + labels: [LGTM_LABEL] + }); + console.log('Label added successfully'); + } else { + console.log(`${LGTM_LABEL} label already exists`); + } + } else if (reviewState === 'changes_requested') { + const lgtmExists = await hasLgtmLabel(); + + if (lgtmExists) { + console.log(`Removing ${LGTM_LABEL} label from PR #${pullRequestNumber}`); + await github.rest.issues.removeLabel({ + ...repoDetails, + name: LGTM_LABEL + }); + console.log('Label removed successfully'); + } else { + console.log(`No ${LGTM_LABEL} label to remove`); + } + } + diff --git a/.github/workflows/auto-delete-branch.yml b/.github/workflows/auto-delete-branch.yml new file mode 100644 index 00000000..b30f5b75 --- /dev/null +++ b/.github/workflows/auto-delete-branch.yml @@ -0,0 +1,39 @@ +name: Auto Delete Merged Branch + +on: + pull_request: + types: [closed] + +jobs: + delete-branch: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Delete merged branch + uses: actions/github-script@v8.0 + with: + script: | + const branchName = context.payload.pull_request.head.ref; + const owner = context.repo.owner; + const repo = context.repo.repo; + + // Don't delete main/master/develop branches + const protectedBranches = ['main', 'master', 'develop']; + if (protectedBranches.includes(branchName)) { + console.log(`Skipping deletion of protected branch: ${branchName}`); + return; + } + + try { + await github.rest.git.deleteRef({ + owner, + repo, + ref: `heads/${branchName}` + }); + console.log(`Successfully deleted branch: ${branchName}`); + } catch (error) { + console.log(`Could not delete branch ${branchName}: ${error.message}`); + } + diff --git a/.github/workflows/community-label.yml b/.github/workflows/community-label.yml new file mode 100644 index 00000000..3cf4a0fc --- /dev/null +++ b/.github/workflows/community-label.yml @@ -0,0 +1,30 @@ +name: Add Community Label + +on: + pull_request_target: + # NOTE: pull_request_target is required to have write permissions to add labels on PRs from forks. + # This workflow must not be modified to check out or execute untrusted PR code, as it runs with base repo permissions. + types: [opened] + +jobs: + add-label: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Add community label + if: github.event.pull_request.author_association != 'MEMBER' && github.event.pull_request.author_association != 'OWNER' && github.event.pull_request.author_association != 'COLLABORATOR' + uses: actions/github-script@v8.0 + with: + script: | + const pullRequestNumber = context.payload.pull_request.number; + const repoDetails = { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pullRequestNumber + }; + await github.rest.issues.addLabels({ + ...repoDetails, + labels: ['community'] + }); + diff --git a/.github/workflows/conventional-labels.yml b/.github/workflows/conventional-labels.yml new file mode 100644 index 00000000..92c1bf36 --- /dev/null +++ b/.github/workflows/conventional-labels.yml @@ -0,0 +1,76 @@ +# NOTE: pull_request_target is required to have write permissions to add labels on PRs from forks. +# This workflow must not be modified to check out or execute untrusted PR code, as it runs with base repo permissions. +# the pull_request_target event. +name: Label PRs with Conventional Commits +on: + pull_request_target: + types: [opened, edited, synchronize] + merge_group: + +jobs: + validate-pr-title: + name: Validate PR Title + runs-on: ubuntu-latest + steps: + - name: Validate PR title follows Conventional Commits + id: validate + uses: Namchee/conventional-pr@v0.15 + with: + access_token: ${{ secrets.GITHUB_TOKEN }} + issue: false + + validate-pr-description: + name: Validate PR Description + runs-on: ubuntu-latest + permissions: + pull-requests: read + steps: + - name: Check PR Description + uses: actions/github-script@v8.0 + with: + script: | + const body = context.payload.pull_request.body || ''; + const title = context.payload.pull_request.title || ''; + const prNumber = context.payload.pull_request.number; + + console.log(`Checking PR #${prNumber}: ${title}`); + + // Skip validation for bot PRs + if (context.payload.pull_request.user.type === 'Bot') { + console.log('Skipping validation for bot PR'); + return; + } + + // Check minimum description length (at least 10 characters) + const minLength = 10; + if (body.trim().length < minLength) { + core.setFailed(`PR description is too short. Please provide a meaningful description (at least ${minLength} characters).`); + return; + } + + // Check for empty or placeholder descriptions + const placeholderPatterns = [ + /^[\s\n]*$/, + /^(n\/a|na|none|no description|todo|tbd|wip)$/i, + /^[\-\*\s]*$/ + ]; + + for (const pattern of placeholderPatterns) { + if (pattern.test(body.trim())) { + core.setFailed('PR description appears to be empty or a placeholder. Please provide a meaningful description.'); + return; + } + } + + console.log('PR description validation passed!'); + + label: + needs: [validate-pr-title] + name: Label PR + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.user.type != 'Bot'}} + steps: + - uses: bcoe/conventional-release-labels@v1 + with: + type_labels: '{"feat": "enhancement","fix": "bug","docs": "documentation","style": "style","refactor": "refactor","perf": "performance","test": "test","chore": "chore","build": "build"}' + diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 00000000..094b3dec --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,17 @@ +name: PR Labeler + +on: + pull_request: + types: [opened, synchronize] + +jobs: + labeler: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/labeler@v6.0 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml new file mode 100644 index 00000000..2a4e8ef5 --- /dev/null +++ b/.github/workflows/pr-checks.yml @@ -0,0 +1,29 @@ +name: PR Title Check + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + +jobs: + validate-title: + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v6.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + feat + fix + docs + style + refactor + perf + test + build + ci + chore + revert + requireScope: false + subjectPattern: ^.+$ +