Skip to content

Commit 3a6bdca

Browse files
Merge pull request #163 from contentstack/enh/dx-6156-adopt-new-release-process
enh(DX-6156): adopt development→main release flow, back-merge, and version checks
2 parents c9e58bd + 6172f3e commit 3a6bdca

6 files changed

Lines changed: 184 additions & 30 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Opens a PR from main → development after changes land on main (back-merge).
2+
3+
name: Back-merge main to development
4+
5+
on:
6+
push:
7+
branches: [main]
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
pull-requests: write
13+
14+
jobs:
15+
open-back-merge-pr:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
23+
- name: Open back-merge PR if needed
24+
env:
25+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26+
run: |
27+
set -euo pipefail
28+
git fetch origin development main
29+
30+
MAIN_SHA=$(git rev-parse origin/main)
31+
DEV_SHA=$(git rev-parse origin/development)
32+
33+
if [ "$MAIN_SHA" = "$DEV_SHA" ]; then
34+
echo "main and development are at the same commit; nothing to back-merge."
35+
exit 0
36+
fi
37+
38+
EXISTING=$(gh pr list --repo "${{ github.repository }}" \
39+
--base development \
40+
--head main \
41+
--state open \
42+
--json number \
43+
--jq 'length')
44+
45+
if [ "$EXISTING" -gt 0 ]; then
46+
echo "An open PR from main to development already exists; skipping."
47+
exit 0
48+
fi
49+
50+
gh pr create --repo "${{ github.repository }}" \
51+
--base development \
52+
--head main \
53+
--title "chore: back-merge main into development" \
54+
--body "Automated back-merge after changes landed on \`main\`. Review and merge to keep \`development\` in sync."
55+
56+
echo "Created back-merge PR main → development."

.github/workflows/check-branch.yml

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Release-affecting changes under Contentstack.Management*/ or Directory.Build.props / core csproj
2+
# require Directory.Build.props Version + CHANGELOG.md updates vs latest tag.
3+
4+
name: Check Version Bump
5+
6+
on:
7+
pull_request:
8+
9+
jobs:
10+
version-bump:
11+
name: Version & changelog bump
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
with:
17+
fetch-depth: 0
18+
19+
- name: Detect changed files
20+
id: detect
21+
run: |
22+
FILES=$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")
23+
echo "Changed files:"
24+
echo "$FILES"
25+
26+
CODE_CHANGED=false
27+
while IFS= read -r f; do
28+
[ -z "$f" ] && continue
29+
case "$f" in
30+
Contentstack.Management.Core.Unit.Tests/*|Contentstack.Management.Core.Tests/*)
31+
continue
32+
;;
33+
esac
34+
if [[ "$f" == "Directory.Build.props" ]] || \
35+
[[ "$f" == Contentstack.Management.Core/* ]] || \
36+
[[ "$f" == Contentstack.Management.ASPNETCore/* ]]; then
37+
CODE_CHANGED=true
38+
break
39+
fi
40+
done <<< "$FILES"
41+
42+
PROPS_CHANGED=false
43+
CHANGELOG_CHANGED=false
44+
echo "$FILES" | grep -qx 'Directory.Build.props' && PROPS_CHANGED=true
45+
echo "$FILES" | grep -qx 'CHANGELOG.md' && CHANGELOG_CHANGED=true
46+
47+
VERSION_FILES_OK=false
48+
if [ "$PROPS_CHANGED" = true ] && [ "$CHANGELOG_CHANGED" = true ]; then
49+
VERSION_FILES_OK=true
50+
fi
51+
52+
echo "code_changed=$CODE_CHANGED" >> "$GITHUB_OUTPUT"
53+
echo "version_files_ok=$VERSION_FILES_OK" >> "$GITHUB_OUTPUT"
54+
55+
- name: Skip when no release-affecting code changed
56+
if: steps.detect.outputs.code_changed != 'true'
57+
run: |
58+
echo "No management SDK / version props changes. Skipping version-bump check."
59+
exit 0
60+
61+
- name: Fail when version files were not both updated
62+
if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_ok != 'true'
63+
run: |
64+
echo "::error::Bump <Version> in Directory.Build.props and add a ## [vX.Y.Z] section at the top of CHANGELOG.md."
65+
exit 1
66+
67+
- name: Validate version vs latest tag and changelog header
68+
if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_ok == 'true'
69+
run: |
70+
set -euo pipefail
71+
PROJ_VERSION=$(python3 <<'PY'
72+
import re
73+
text = open("Directory.Build.props").read()
74+
m = re.search(r"<Version>\s*([^<]+)\s*</Version>", text)
75+
if not m:
76+
raise SystemExit("Could not read <Version> from Directory.Build.props")
77+
print(m.group(1).strip())
78+
PY
79+
)
80+
81+
git fetch --tags --force 2>/dev/null || true
82+
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || true)
83+
if [ -z "$LATEST_TAG" ]; then
84+
echo "No existing tags found. Skipping semver vs tag check."
85+
CHANGELOG_HEAD=$(sed -nE 's/^## \[v?([0-9]+\.[0-9]+\.[0-9]+)\].*/\1/p' CHANGELOG.md | head -1)
86+
if [ -z "$CHANGELOG_HEAD" ]; then
87+
echo "::error::Could not find a ## [vX.Y.Z] entry at the top of CHANGELOG.md."
88+
exit 1
89+
fi
90+
if [ "$CHANGELOG_HEAD" != "$PROJ_VERSION" ]; then
91+
echo "::error::CHANGELOG top version ($CHANGELOG_HEAD) does not match Directory.Build.props Version ($PROJ_VERSION)."
92+
exit 1
93+
fi
94+
exit 0
95+
fi
96+
97+
LATEST_VERSION="${LATEST_TAG#v}"
98+
LATEST_VERSION="${LATEST_VERSION%%-*}"
99+
if [ "$(printf '%s\n' "$LATEST_VERSION" "$PROJ_VERSION" | sort -V | tail -1)" != "$PROJ_VERSION" ]; then
100+
echo "::error::Version ($PROJ_VERSION) must be greater than latest tag ($LATEST_TAG)."
101+
exit 1
102+
fi
103+
if [ "$PROJ_VERSION" = "$LATEST_VERSION" ]; then
104+
echo "::error::Version ($PROJ_VERSION) must be strictly greater than latest tag version ($LATEST_VERSION)."
105+
exit 1
106+
fi
107+
108+
CHANGELOG_HEAD=$(sed -nE 's/^## \[v?([0-9]+\.[0-9]+\.[0-9]+)\].*/\1/p' CHANGELOG.md | head -1)
109+
if [ -z "$CHANGELOG_HEAD" ]; then
110+
echo "::error::Could not find a ## [vX.Y.Z] entry at the top of CHANGELOG.md."
111+
exit 1
112+
fi
113+
if [ "$CHANGELOG_HEAD" != "$PROJ_VERSION" ]; then
114+
echo "::error::CHANGELOG top version ($CHANGELOG_HEAD) does not match Directory.Build.props Version ($PROJ_VERSION)."
115+
exit 1
116+
fi
117+
echo "Version bump check passed: Directory.Build.props and CHANGELOG at $PROJ_VERSION (latest tag: $LATEST_TAG)."

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
| **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)). |
3030
| **Pack (release)** | `dotnet pack -c Release -o out` (as in [`.github/workflows/nuget-publish.yml`](.github/workflows/nuget-publish.yml)). |
3131

32-
**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).
32+
**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.
3333

3434
## Where the documentation lives: skills
3535

skills/code-review/SKILL.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ description: Use when reviewing or preparing a pull request for contentstack-man
1414

1515
### Branch and merge expectations
1616

17-
- **Typical PRs** should target **`development`**. Use **`main`** as the base branch only for **hotfixes**.
18-
- **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.
17+
- **Feature/fix PRs** target **`development`**. **Release PRs** use **`development``main`** (no `staging`). Confirm head/base match the intended step in that flow before approving.
1918

2019
### Summary checklist
2120

@@ -42,8 +41,8 @@ Copy sections into a PR comment when useful. This checklist is for **this** repo
4241
#### Branch policy
4342

4443
```markdown
45-
- [ ] **Default:** PR targets **`development`** unless this is a documented **hotfix** to **`main`**
46-
- [ ] If base is **`main`**: head branch is **`staging`** (see `.github/workflows/check-branch.yml`)
44+
- [ ] **Feature/fix:** PR targets **`development`**
45+
- [ ] **Release:** if merging to **`main`**, this is the agreed **`development``main`** release PR (not a bypass of version/tag checks)
4746
```
4847

4948
#### Breaking changes

skills/dev-workflow/SKILL.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@ description: Use for branches, CI, build/test scripts, and NuGet release flow in
1515

1616
### Branch policy
1717

18-
- **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).
19-
- **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.
20-
- Do not bypass enforced checks without org approval.
18+
- **Default:** open PRs against **`development`** for feature and fix work.
19+
- **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.
20+
- **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`).
21+
- **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).
2122

2223
### Key workflows
2324

2425
| Workflow | Role |
2526
| -------- | ---- |
2627
| [`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). |
27-
| [`check-branch.yml`](../../.github/workflows/check-branch.yml) | For PRs **into `main`**, enforces head branch **`staging`**. |
28-
| [`nuget-publish.yml`](../../.github/workflows/nuget-publish.yml) | On release: `dotnet pack -c Release -o out` and push to NuGet / GitHub Packages. |
28+
| [`back-merge-pr.yml`](../../.github/workflows/back-merge-pr.yml) | After pushes to **`main`**, opens **`main``development`** PR if needed. |
29+
| [`check-version-bump.yml`](../../.github/workflows/check-version-bump.yml) | On PR: requires version + changelog when SDK sources / props change. |
30+
| [`nuget-publish.yml`](../../.github/workflows/nuget-publish.yml) | On **GitHub Release** (`created`): `dotnet pack -c Release -o out` and push to NuGet / GitHub Packages. |
2931
| [`policy-scan.yml`](../../.github/workflows/policy-scan.yml), [`sca-scan.yml`](../../.github/workflows/sca-scan.yml) | Security / compliance scans. |
3032

3133
### Local commands

0 commit comments

Comments
 (0)