From 6172f3ec6fdcd11284758b0cec3abe1a5b4d2dbf Mon Sep 17 00:00:00 2001 From: harshitha-cstk Date: Thu, 23 Apr 2026 17:12:03 +0530 Subject: [PATCH] =?UTF-8?q?enh:=20adopt=20development=E2=86=92main=20relea?= =?UTF-8?q?se=20flow,=20back-merge,=20and=20version=20checks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/back-merge-pr.yml | 56 +++++++++++ .github/workflows/check-branch.yml | 20 ---- .github/workflows/check-version-bump.yml | 117 +++++++++++++++++++++++ AGENTS.md | 2 +- skills/code-review/SKILL.md | 7 +- skills/dev-workflow/SKILL.md | 12 ++- 6 files changed, 184 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/back-merge-pr.yml delete mode 100644 .github/workflows/check-branch.yml create mode 100644 .github/workflows/check-version-bump.yml diff --git a/.github/workflows/back-merge-pr.yml b/.github/workflows/back-merge-pr.yml new file mode 100644 index 0000000..e9ac740 --- /dev/null +++ b/.github/workflows/back-merge-pr.yml @@ -0,0 +1,56 @@ +# Opens a PR from main → development after changes land on main (back-merge). + +name: Back-merge main to development + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + pull-requests: write + +jobs: + open-back-merge-pr: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Open back-merge PR if needed + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + git fetch origin development main + + MAIN_SHA=$(git rev-parse origin/main) + DEV_SHA=$(git rev-parse origin/development) + + if [ "$MAIN_SHA" = "$DEV_SHA" ]; then + echo "main and development are at the same commit; nothing to back-merge." + exit 0 + fi + + EXISTING=$(gh pr list --repo "${{ github.repository }}" \ + --base development \ + --head main \ + --state open \ + --json number \ + --jq 'length') + + if [ "$EXISTING" -gt 0 ]; then + echo "An open PR from main to development already exists; skipping." + exit 0 + fi + + gh pr create --repo "${{ github.repository }}" \ + --base development \ + --head main \ + --title "chore: back-merge main into development" \ + --body "Automated back-merge after changes landed on \`main\`. Review and merge to keep \`development\` in sync." + + echo "Created back-merge PR main → development." diff --git a/.github/workflows/check-branch.yml b/.github/workflows/check-branch.yml deleted file mode 100644 index 00a6a8a..0000000 --- a/.github/workflows/check-branch.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: 'Check Branch' - -on: - pull_request: - -jobs: - check_branch: - runs-on: ubuntu-latest - steps: - - name: Comment PR - if: github.base_ref == 'main' && github.head_ref != 'staging' - uses: thollander/actions-comment-pull-request@v2 - with: - message: | - We regret to inform you that you are currently not able to merge your changes into the master branch due to restrictions applied by our SRE team. To proceed with merging your changes, we kindly request that you create a pull request from the next branch. Our team will then review the changes and work with you to ensure a successful merge into the master branch. - - name: Check branch - if: github.base_ref == 'main' && github.head_ref != 'staging' - run: | - echo "ERROR: We regret to inform you that you are currently not able to merge your changes into the master branch due to restrictions applied by our SRE team. To proceed with merging your changes, we kindly request that you create a pull request from the next branch. Our team will then review the changes and work with you to ensure a successful merge into the master branch." - exit 1 diff --git a/.github/workflows/check-version-bump.yml b/.github/workflows/check-version-bump.yml new file mode 100644 index 0000000..658d336 --- /dev/null +++ b/.github/workflows/check-version-bump.yml @@ -0,0 +1,117 @@ +# Release-affecting changes under Contentstack.Management*/ or Directory.Build.props / core csproj +# require Directory.Build.props Version + CHANGELOG.md updates vs latest tag. + +name: Check Version Bump + +on: + pull_request: + +jobs: + version-bump: + name: Version & changelog bump + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Detect changed files + id: detect + run: | + FILES=$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}") + echo "Changed files:" + echo "$FILES" + + CODE_CHANGED=false + while IFS= read -r f; do + [ -z "$f" ] && continue + case "$f" in + Contentstack.Management.Core.Unit.Tests/*|Contentstack.Management.Core.Tests/*) + continue + ;; + esac + if [[ "$f" == "Directory.Build.props" ]] || \ + [[ "$f" == Contentstack.Management.Core/* ]] || \ + [[ "$f" == Contentstack.Management.ASPNETCore/* ]]; then + CODE_CHANGED=true + break + fi + done <<< "$FILES" + + PROPS_CHANGED=false + CHANGELOG_CHANGED=false + echo "$FILES" | grep -qx 'Directory.Build.props' && PROPS_CHANGED=true + echo "$FILES" | grep -qx 'CHANGELOG.md' && CHANGELOG_CHANGED=true + + VERSION_FILES_OK=false + if [ "$PROPS_CHANGED" = true ] && [ "$CHANGELOG_CHANGED" = true ]; then + VERSION_FILES_OK=true + fi + + echo "code_changed=$CODE_CHANGED" >> "$GITHUB_OUTPUT" + echo "version_files_ok=$VERSION_FILES_OK" >> "$GITHUB_OUTPUT" + + - name: Skip when no release-affecting code changed + if: steps.detect.outputs.code_changed != 'true' + run: | + echo "No management SDK / version props changes. Skipping version-bump check." + exit 0 + + - name: Fail when version files were not both updated + if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_ok != 'true' + run: | + echo "::error::Bump in Directory.Build.props and add a ## [vX.Y.Z] section at the top of CHANGELOG.md." + exit 1 + + - name: Validate version vs latest tag and changelog header + if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_ok == 'true' + run: | + set -euo pipefail + PROJ_VERSION=$(python3 <<'PY' + import re + text = open("Directory.Build.props").read() + m = re.search(r"\s*([^<]+)\s*", text) + if not m: + raise SystemExit("Could not read from Directory.Build.props") + print(m.group(1).strip()) + PY + ) + + git fetch --tags --force 2>/dev/null || true + LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || true) + if [ -z "$LATEST_TAG" ]; then + echo "No existing tags found. Skipping semver vs tag check." + CHANGELOG_HEAD=$(sed -nE 's/^## \[v?([0-9]+\.[0-9]+\.[0-9]+)\].*/\1/p' CHANGELOG.md | head -1) + if [ -z "$CHANGELOG_HEAD" ]; then + echo "::error::Could not find a ## [vX.Y.Z] entry at the top of CHANGELOG.md." + exit 1 + fi + if [ "$CHANGELOG_HEAD" != "$PROJ_VERSION" ]; then + echo "::error::CHANGELOG top version ($CHANGELOG_HEAD) does not match Directory.Build.props Version ($PROJ_VERSION)." + exit 1 + fi + exit 0 + fi + + LATEST_VERSION="${LATEST_TAG#v}" + LATEST_VERSION="${LATEST_VERSION%%-*}" + if [ "$(printf '%s\n' "$LATEST_VERSION" "$PROJ_VERSION" | sort -V | tail -1)" != "$PROJ_VERSION" ]; then + echo "::error::Version ($PROJ_VERSION) must be greater than latest tag ($LATEST_TAG)." + exit 1 + fi + if [ "$PROJ_VERSION" = "$LATEST_VERSION" ]; then + echo "::error::Version ($PROJ_VERSION) must be strictly greater than latest tag version ($LATEST_VERSION)." + exit 1 + fi + + CHANGELOG_HEAD=$(sed -nE 's/^## \[v?([0-9]+\.[0-9]+\.[0-9]+)\].*/\1/p' CHANGELOG.md | head -1) + if [ -z "$CHANGELOG_HEAD" ]; then + echo "::error::Could not find a ## [vX.Y.Z] entry at the top of CHANGELOG.md." + exit 1 + fi + if [ "$CHANGELOG_HEAD" != "$PROJ_VERSION" ]; then + echo "::error::CHANGELOG top version ($CHANGELOG_HEAD) does not match Directory.Build.props Version ($PROJ_VERSION)." + exit 1 + fi + echo "Version bump check passed: Directory.Build.props and CHANGELOG at $PROJ_VERSION (latest tag: $LATEST_TAG)." diff --git a/AGENTS.md b/AGENTS.md index 1a8fcbe..1a45d88 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,7 +29,7 @@ | **Test (integration)** | `dotnet test Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj` — requires local `appsettings.json` with credentials (see [`skills/testing/SKILL.md`](skills/testing/SKILL.md)). | | **Pack (release)** | `dotnet pack -c Release -o out` (as in [`.github/workflows/nuget-publish.yml`](.github/workflows/nuget-publish.yml)). | -**CI:** [`.github/workflows/unit-test.yml`](.github/workflows/unit-test.yml) (unit tests on PR/push). **Branches:** PRs normally target **`development`**; **`main`** is for **hotfixes**. PRs **into `main`** must come from **`staging`** per [`.github/workflows/check-branch.yml`](.github/workflows/check-branch.yml). +**CI:** [`.github/workflows/unit-test.yml`](.github/workflows/unit-test.yml) (unit tests on PR/push). **Branches:** feature work merges to **`development`**; **release PRs** are **`development` → `main`** (no `staging`). After `main` advances, [`.github/workflows/back-merge-pr.yml`](.github/workflows/back-merge-pr.yml) can open **`main` → `development`**. **Releases:** create a **GitHub Release** to run [`.github/workflows/nuget-publish.yml`](.github/workflows/nuget-publish.yml) (`release: created`). [`.github/workflows/check-version-bump.yml`](.github/workflows/check-version-bump.yml) enforces version + `CHANGELOG.md` on relevant PRs. ## Where the documentation lives: skills diff --git a/skills/code-review/SKILL.md b/skills/code-review/SKILL.md index 62e62e6..e3495f6 100644 --- a/skills/code-review/SKILL.md +++ b/skills/code-review/SKILL.md @@ -14,8 +14,7 @@ description: Use when reviewing or preparing a pull request for contentstack-man ### Branch and merge expectations -- **Typical PRs** should target **`development`**. Use **`main`** as the base branch only for **hotfixes**. -- **When the base is `main`:** only PRs from **`staging`** are allowed (enforced by [`.github/workflows/check-branch.yml`](../../.github/workflows/check-branch.yml)). Confirm head/base match team intent before approving. +- **Feature/fix PRs** target **`development`**. **Release PRs** use **`development` → `main`** (no `staging`). Confirm head/base match the intended step in that flow before approving. ### Summary checklist @@ -42,8 +41,8 @@ Copy sections into a PR comment when useful. This checklist is for **this** repo #### Branch policy ```markdown -- [ ] **Default:** PR targets **`development`** unless this is a documented **hotfix** to **`main`** -- [ ] If base is **`main`**: head branch is **`staging`** (see `.github/workflows/check-branch.yml`) +- [ ] **Feature/fix:** PR targets **`development`** +- [ ] **Release:** if merging to **`main`**, this is the agreed **`development` → `main`** release PR (not a bypass of version/tag checks) ``` #### Breaking changes diff --git a/skills/dev-workflow/SKILL.md b/skills/dev-workflow/SKILL.md index 68fef13..786617d 100644 --- a/skills/dev-workflow/SKILL.md +++ b/skills/dev-workflow/SKILL.md @@ -15,17 +15,19 @@ description: Use for branches, CI, build/test scripts, and NuGet release flow in ### Branch policy -- **Default workflow:** open PRs against **`development`** for regular feature and fix work. **`main`** is reserved for **hotfixes** (PRs raised directly to `main` only when patching production). -- **When the PR target is `main`:** GitHub Actions requires the head branch to be **`staging`**—other head branches are rejected by [`.github/workflows/check-branch.yml`](../../.github/workflows/check-branch.yml). Coordinate with SRE/release if a hotfix must use a different flow. -- Do not bypass enforced checks without org approval. +- **Default:** open PRs against **`development`** for feature and fix work. +- **Releases:** open a **release PR `development` → `main`** (no `staging`). After `main` is updated, [`.github/workflows/back-merge-pr.yml`](../../.github/workflows/back-merge-pr.yml) opens **`main` → `development`** when needed so branches stay aligned. +- **Publishing:** create a **GitHub Release** (after the release commit is on `main`) to trigger [`.github/workflows/nuget-publish.yml`](../../.github/workflows/nuget-publish.yml) (`release: created`). +- **Version gate:** PRs that touch product code or `Directory.Build.props` need matching bumps in `Directory.Build.props` and `CHANGELOG.md` per [`.github/workflows/check-version-bump.yml`](../../.github/workflows/check-version-bump.yml). ### Key workflows | Workflow | Role | | -------- | ---- | | [`unit-test.yml`](../../.github/workflows/unit-test.yml) | On PR and push: runs [`Scripts/run-unit-test-case.sh`](../../Scripts/run-unit-test-case.sh) (unit tests + TRX + coverlet). | -| [`check-branch.yml`](../../.github/workflows/check-branch.yml) | For PRs **into `main`**, enforces head branch **`staging`**. | -| [`nuget-publish.yml`](../../.github/workflows/nuget-publish.yml) | On release: `dotnet pack -c Release -o out` and push to NuGet / GitHub Packages. | +| [`back-merge-pr.yml`](../../.github/workflows/back-merge-pr.yml) | After pushes to **`main`**, opens **`main` → `development`** PR if needed. | +| [`check-version-bump.yml`](../../.github/workflows/check-version-bump.yml) | On PR: requires version + changelog when SDK sources / props change. | +| [`nuget-publish.yml`](../../.github/workflows/nuget-publish.yml) | On **GitHub Release** (`created`): `dotnet pack -c Release -o out` and push to NuGet / GitHub Packages. | | [`policy-scan.yml`](../../.github/workflows/policy-scan.yml), [`sca-scan.yml`](../../.github/workflows/sca-scan.yml) | Security / compliance scans. | ### Local commands