Skip to content

Commit ee61dd4

Browse files
authored
Merge pull request #39 from contentstack/fix/DX-6185
chore: standardize ruby release flow with back-merge and strict version checks
2 parents c9deb45 + 23bc113 commit ee61dd4

7 files changed

Lines changed: 142 additions & 24 deletions

File tree

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

.github/workflows/check-branch.yml

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Runs only when production code under lib/ changes. Version must be > latest v* tag (not vs base branch).
2+
3+
name: Check Version Bump
4+
5+
on:
6+
pull_request:
7+
8+
jobs:
9+
check-version-bump:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
with:
14+
fetch-depth: 0
15+
- name: Validate version and changelog updates
16+
shell: bash
17+
run: |
18+
set -euo pipefail
19+
20+
VERSION_FILE="lib/contentstack_utils/version.rb"
21+
CHANGELOG_FILE="CHANGELOG.md"
22+
BASE_SHA="${{ github.event.pull_request.base.sha }}"
23+
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
24+
25+
mapfile -t CHANGED_FILES < <(git diff --name-only "$BASE_SHA" "$HEAD_SHA")
26+
if [ "${#CHANGED_FILES[@]}" -eq 0 ]; then
27+
echo "No changed files detected."
28+
exit 0
29+
fi
30+
31+
is_production_source_change() {
32+
local f="$1"
33+
[[ "$f" == lib/* ]]
34+
}
35+
36+
has_source_changes=false
37+
for file in "${CHANGED_FILES[@]}"; do
38+
if is_production_source_change "$file"; then
39+
has_source_changes=true
40+
break
41+
fi
42+
done
43+
44+
if [ "$has_source_changes" = false ]; then
45+
echo "Skipping: no lib/ production code changes."
46+
exit 0
47+
fi
48+
49+
changed_file() {
50+
local target="$1"
51+
for file in "${CHANGED_FILES[@]}"; do
52+
if [ "$file" = "$target" ]; then
53+
return 0
54+
fi
55+
done
56+
return 1
57+
}
58+
59+
changed_file "$VERSION_FILE" || { echo "Version bump required in $VERSION_FILE."; exit 1; }
60+
changed_file "$CHANGELOG_FILE" || { echo "Matching changelog update required in $CHANGELOG_FILE."; exit 1; }
61+
62+
head_version=$(sed -nE 's/.*VERSION\s*=\s*["'"'"']([^"'"'"']+)["'"'"'].*/\1/p' "$VERSION_FILE" | sed -n '1p')
63+
CHANGELOG_HEAD=$(sed -nE 's/^## v?([^[:space:]]+).*/\1/p' "$CHANGELOG_FILE" | head -1)
64+
65+
[ -n "$CHANGELOG_HEAD" ] || { echo "::error::Could not find a top changelog heading like '## vX.Y.Z' in $CHANGELOG_FILE."; exit 1; }
66+
[ "$CHANGELOG_HEAD" = "$head_version" ] || { echo "::error::$CHANGELOG_FILE top version ($CHANGELOG_HEAD) does not match project version ($head_version)."; exit 1; }
67+
68+
latest_tag=$(git tag --list 'v*' --sort=-version:refname | sed -n '1p')
69+
latest_version="${latest_tag#v}"
70+
[ -n "$latest_version" ] || latest_version="0.0.0"
71+
72+
version_gt() {
73+
python3 -c 'import sys;v=lambda s:[int(x) if x.isdigit() else 0 for x in (s.strip().lstrip("v").split("-",1)[0].split("+",1)[0].split(".")+["0","0","0"])[:3]];print("true" if v(sys.argv[1])>v(sys.argv[2]) else "false")' "$1" "$2"
74+
}
75+
76+
[ "$(version_gt "$head_version" "$latest_version")" = "true" ] || { echo "Version must be greater than latest tag version ($latest_version). Found $head_version."; exit 1; }

.github/workflows/release-gem.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66

77
jobs:
88
build:
9+
if: ${{ startsWith(github.event.release.tag_name, 'v') && !github.event.release.draft }}
910
name: Build + Publish
1011
runs-on: ubuntu-latest
1112
permissions:
@@ -14,6 +15,8 @@ jobs:
1415

1516
steps:
1617
- uses: actions/checkout@v3
18+
with:
19+
ref: ${{ github.event.release.tag_name }}
1720
- name: Set up Ruby 2.7
1821
uses: ruby/setup-ruby@v1
1922
with:

skills/code-review/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ description: Use when reviewing or preparing a PR for this gem—behavior, tests
3333

3434
### Process
3535

36-
- Respect **CODEOWNERS** and branch policy (**`master`** vs **`staging`**) described in [dev-workflow](../dev-workflow/SKILL.md)
36+
- Respect **CODEOWNERS** and branch policy (**feature/fix -> `development`**, release PRs **`development` -> `master`**) described in [dev-workflow](../dev-workflow/SKILL.md)
3737

3838
## References
3939

skills/dev-workflow/SKILL.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ description: Use when setting up the dev environment, running build/test/docs, o
3232

3333
### Branches and PRs
3434

35-
- `.github/workflows/check-branch.yml` blocks merging into **`master`** unless the head branch is **`staging`** (organizational policy). Prefer PRs that follow team conventions for `master` / `staging`.
35+
- Feature/fix PRs should target **`development`**. Release PRs are raised directly from **`development`** to **`master`**.
3636
- Use `CODEOWNERS` for required reviewers when applicable
3737

3838
### CI and automation (no RSpec workflow today)
3939

40-
- **Release:** `.github/workflows/release-gem.yml` — on GitHub **release created**, builds and pushes to RubyGems (note: workflow pins Ruby 2.7 for publish; align with gemspec minimum when changing)
40+
- **Release:** `.github/workflows/release-gem.yml` — on GitHub **Release** created (`release: types: [created]`) for tag **`v*`** (draft releases skipped), checks out the tag, then builds and pushes to RubyGems (note: workflow pins Ruby 2.7 for publish; align with gemspec minimum when changing)
4141
- **Security / compliance:** CodeQL, policy scan, SCA scan — see `.github/workflows/`
4242
- **Issues:** Jira integration workflow in `.github/workflows/issues-jira.yml`
4343

skills/framework/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ description: Use when changing the gemspec, Bundler setup, Ruby/runtime constrai
3535
### Build and publish
3636

3737
- Local artifact: `gem build contentstack_utils.gemspec`
38-
- Publishing is triggered by GitHub **release** per `release-gem.yml`
38+
- Publishing runs when a GitHub **Release** is created (`release: types: [created]`) for tag **`v*`** (draft releases skipped); see `release-gem.yml`.
3939

4040
## References
4141

0 commit comments

Comments
 (0)