diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..e10d702 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,29 @@ +## CHANGELOG +REPLACE_ME_WITH_CHANGELOG + +## SUMMARY +REPLACE_ME_WITH_SUMMARY_OF_THE_CHANGES + +## FUNCTIONAL AUTOMATION CHANGES PR +- [ ] Yes + - If Yes, PR : +- [ ] No + - If No, Reason: + +## AUTOMATION TEST REPORT URL +REPLACE_ME_WITH_TEST_REPORT_URL + +## AREAS OF IMPACT +REPLACE_ME_WITH_AREAS_OF_IMPACT_OR_NA + +## TYPE OF CHANGE +- [ ] ๐Ÿž Bugfix +- [ ] ๐ŸŒŸ Feature +- [ ] โœจ Enhancement +- [ ] ๐Ÿงช Unit Test Cases +- [ ] ๐Ÿ“” Documentation +- [ ] โš™๏ธ Chore - Build Related / Configuration / Others + + +## DOCUMENTATION +REPLACE_ME_WITH_DOCUMENTATION_LINK_OR_NA \ No newline at end of file diff --git a/.github/scripts/README.md b/.github/scripts/README.md new file mode 100644 index 0000000..6a4a49f --- /dev/null +++ b/.github/scripts/README.md @@ -0,0 +1,31 @@ +# Scripts + +## Generate changelog + +Lists PRs merged into a branch (excludes "Parent branch sync" and bot-authored PRs). + +**Prerequisites:** `jq`, and GitHub credentials as env vars. + +### Set credentials (once per terminal) + +```bash +export GH_USERNAME=your-github-username +export GH_PAT=your-github-personal-access-token +``` + +### Commands + +From repo root: + +| What you want | Command | +|---------------|---------| +| PRs merged into **current branch** (last 30 days) | `./.github/scripts/generate-changelog.sh` | +| PRs merged into **master** (last 30 days) | `./.github/scripts/generate-changelog.sh master` | +| PRs merged into **master** since a date | `./.github/scripts/generate-changelog.sh master "merged:>=2025-01-01"` | + +### Arguments + +1. **Branch** (optional) โ€” default: current branch +2. **Date filter** (optional) โ€” default: last 30 days (e.g. `merged:>=2025-01-01`) + +Output includes a GitHub search URL to verify results. diff --git a/.github/scripts/generate-changelog.sh b/.github/scripts/generate-changelog.sh new file mode 100644 index 0000000..78b3aa7 --- /dev/null +++ b/.github/scripts/generate-changelog.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +set -e + +# Check required environment variables +if [[ -z "$GH_USERNAME" ]]; then + echo "โŒ Error: Environment variable GH_USERNAME not found" + exit 1 +fi + +if [[ -z "$GH_PAT" ]]; then + echo "โŒ Error: Environment variable GH_PAT not found" + exit 1 +fi + +# Optional: Branch name (defaults to current branch if not provided) +SOURCE_BRANCH="${1:-$(git branch --show-current)}" +# Optional: Date filter (defaults to last 30 days if not provided) +DATE_FILTER="${2:-merged:>=$(date -u -v-30d +%Y-%m-%d 2>/dev/null || date -u -d '30 days ago' +%Y-%m-%d)}" + +# Repo is set per-repo when this file is pushed (placeholder replaced by upload script) +REPO="chargebee/chargebee-react-native" + +echo "๐Ÿ” Searching for PRs merged into $SOURCE_BRANCH..." + +# GitHub API call with error handling +HTTP_STATUS=$(curl -s -w "%{http_code}" -G -u "$GH_USERNAME:$GH_PAT" \ + "https://api.github.com/search/issues" \ + --data-urlencode "q=NOT \"Parent branch sync\" in:title is:pr repo:$REPO is:merged base:$SOURCE_BRANCH merged:$DATE_FILTER -author:app/distributed-gitflow-app" \ + -o /tmp/curl_output.json \ + 2>/tmp/curl_error.log) + +CURL_EXIT_CODE=$? + +echo "๐ŸŒ API call status: $HTTP_STATUS" + +if [ $CURL_EXIT_CODE -ne 0 ]; then + echo "โŒ Error: curl request failed with exit code $CURL_EXIT_CODE" + echo "Error details: $(cat /tmp/curl_error.log)" + exit 1 +fi + +if [ "$HTTP_STATUS" -ne 200 ]; then + echo "โŒ Error: API returned HTTP status $HTTP_STATUS" + echo "Response: $(cat /tmp/curl_output.json)" + exit 1 +fi + +PR_LIST_RESPONSE=$(cat /tmp/curl_output.json) + +# Clean invalid control characters from JSON response +if ! echo "$PR_LIST_RESPONSE" | jq . >/dev/null 2>&1; then + echo "โš ๏ธ Invalid JSON detected โ€” cleaning control characters..." + PR_LIST_RESPONSE=$(echo "$PR_LIST_RESPONSE" | tr -d '\000-\037') + + if ! echo "$PR_LIST_RESPONSE" | jq . >/dev/null 2>&1; then + echo "$PR_LIST_RESPONSE" > /tmp/invalid_json_debug.json + echo "โŒ Error: JSON is still invalid after cleaning control characters" + echo "๐Ÿ’ก Use 'cat /tmp/invalid_json_debug.json' to inspect the JSON" + exit 1 + fi +fi + +PR_MERGED_COUNT=$(echo "$PR_LIST_RESPONSE" | jq '.total_count') + +# Color codes +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NOCOLOR='\033[0m' + +echo "==============================================================================" +echo -e "Found ${GREEN}$PR_MERGED_COUNT${NOCOLOR} PR(s) merged into $SOURCE_BRANCH (filter: $DATE_FILTER)" +echo "==============================================================================" +echo -e "## ${GREEN}CHANGELOG${NOCOLOR}" +echo "$PR_LIST_RESPONSE" | jq -r '.items[] | (.title) + " (" + (.user.login) + ") [#" + (.number | tostring) + "]"' | sort +printf "\n" +echo "==============================================================================" +echo -e "${GREEN}GitHub Search URL (to verify, if required)${NOCOLOR}" +BRANCH_ENCODED=$(echo "$SOURCE_BRANCH" | sed 's/ /%20/g') +echo "https://github.com/$REPO/pulls?q=NOT+%22Parent+branch+sync%22+in%3Atitle+is%3Apr+is%3Amerged+base%3A$BRANCH_ENCODED+merged%3A$DATE_FILTER+-author%3Aapp%2Fdistributed-gitflow-app" +echo "==============================================================================" + +# Clean up temporary files +rm -f /tmp/curl_output.json /tmp/curl_error.log diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml new file mode 100644 index 0000000..42e00e0 --- /dev/null +++ b/.github/workflows/pr-lint.yml @@ -0,0 +1,13 @@ +name: Common PR Lint + +on: + pull_request: + branches: [master, main,staging, dev,develop] + types: [ready_for_review, reopened, review_requested, review_request_removed, opened, edited] + +jobs: + pr-lint: + name: Common PR Lint Checks + if: github.base_ref == 'main' || github.base_ref == 'master' + uses: chargebee/cb-cicd-pipelines/.github/workflows/pr-lint.yml@main + secrets: inherit diff --git a/.github/workflows/pr-size-check.yml b/.github/workflows/pr-size-check.yml new file mode 100644 index 0000000..33e02bb --- /dev/null +++ b/.github/workflows/pr-size-check.yml @@ -0,0 +1,60 @@ +name: PR Size Check +on: + pull_request: + types: [ reopened, opened, synchronize, edited, labeled, unlabeled ] + branches: + - develop/** + - dev + + +jobs: + pre-approval-comment: + name: Announce pending bypass approval + if: ${{ github.event.pull_request.user.login != 'distributed-gitflow-app[bot]' && + !startsWith(github.head_ref, 'revert-') && + !startsWith(github.head_ref, 'parent-branch-sync/') && + contains(github.event.pull_request.labels.*.name, 'pr-size-exception') }} + runs-on: graviton-small-runner + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const issue_number = context.payload.pull_request.number; + + const marker = ''; + const pending = `${marker} + ๐Ÿ›‘ The \`pr-size-exception\` label is present. This workflow is **waiting for approvals** from the **[cb-Billing-CAB-reviewers](https://github.com/orgs/chargebee/teams/cb-billing-cab-approvers)**.`; + + // create a new comment when the workflow runs + await github.rest.issues.createComment({ owner, repo, issue_number, body: pending }); + pr-size-check: + name: Check PR size + if: ${{ (github.base_ref == 'dev' || startsWith(github.base_ref, 'develop/')) && github.event.pull_request.user.login != 'distributed-gitflow-app[bot]' && !startsWith(github.head_ref, 'revert-') && !startsWith(github.head_ref, 'parent-branch-sync/') }} + runs-on: graviton-small-runner + permissions: + contents: read + pull-requests: write + env: + BYPASS_LABEL: pr-size-exception + environment: ${{ contains(github.event.pull_request.labels.*.name, 'pr-size-exception') && 'cb-billing-reviewers' || '' }} + steps: + - uses: chargebee/cb-cicd-pipelines/.github/actions/pr-size-check@v4.20.3 + if: ${{ !contains(github.event.pull_request.labels.*.name, env.BYPASS_LABEL) }} + with: + githubToken: ${{ secrets.GITHUB_TOKEN }} + errorSize: 250 + warningSize: 200 + excludePaths: | + .github/** + .cursor/** + + + - name: Ensure required check passes when bypassed + if: ${{ contains(github.event.pull_request.labels.*.name, env.BYPASS_LABEL) }} + run: echo "Bypass active โ€” marking job successful."