Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/back-merge-pr.yml
Original file line number Diff line number Diff line change
@@ -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."
20 changes: 0 additions & 20 deletions .github/workflows/check-branch.yml

This file was deleted.

117 changes: 117 additions & 0 deletions .github/workflows/check-version-bump.yml
Original file line number Diff line number Diff line change
@@ -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 <Version> 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"<Version>\s*([^<]+)\s*</Version>", text)
if not m:
raise SystemExit("Could not read <Version> 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)."
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 3 additions & 4 deletions skills/code-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down
12 changes: 7 additions & 5 deletions skills/dev-workflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading