Skip to content

Commit 31516d9

Browse files
Merge pull request #297 from contentstack/development
DX | 15-06-2026 | Release
2 parents 4bb4b36 + 54b7bd9 commit 31516d9

15 files changed

Lines changed: 1196 additions & 61 deletions

File tree

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

.github/workflows/check-branch.yml

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: Check Version Bump
2+
3+
on:
4+
pull_request:
5+
6+
jobs:
7+
version-bump:
8+
name: Version & Changelog bump
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Checkout
12+
uses: actions/checkout@v4
13+
with:
14+
fetch-depth: 0
15+
16+
- name: Detect changed files and version bump
17+
id: detect
18+
run: |
19+
if git rev-parse HEAD^2 >/dev/null 2>&1; then
20+
FILES=$(git diff --name-only HEAD^1 HEAD^2)
21+
else
22+
FILES=$(git diff --name-only HEAD~1 HEAD)
23+
fi
24+
VERSION_FILES_CHANGED=false
25+
echo "$FILES" | grep -qx 'package.json' && VERSION_FILES_CHANGED=true
26+
echo "$FILES" | grep -qx 'CHANGELOG.md' && VERSION_FILES_CHANGED=true
27+
echo "version_files_changed=$VERSION_FILES_CHANGED" >> $GITHUB_OUTPUT
28+
# Only lib/, webpack/, dist/, package.json count as release-affecting; .github/ and test/ do not
29+
CODE_CHANGED=false
30+
echo "$FILES" | grep -qE '^lib/|^webpack/|^dist/' && CODE_CHANGED=true
31+
echo "$FILES" | grep -qx 'package.json' && CODE_CHANGED=true
32+
echo "code_changed=$CODE_CHANGED" >> $GITHUB_OUTPUT
33+
34+
- name: Skip when only test/docs/.github changed
35+
if: steps.detect.outputs.code_changed != 'true'
36+
run: |
37+
echo "No release-affecting files changed (e.g. only test/docs/.github). Skipping version-bump check."
38+
exit 0
39+
40+
- name: Fail when version bump was missed
41+
if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed != 'true'
42+
run: |
43+
echo "::error::This PR has code changes but no version bump. Please bump the version in package.json and add an entry in CHANGELOG.md."
44+
exit 1
45+
46+
- name: Setup Node
47+
if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed == 'true'
48+
uses: actions/setup-node@v4
49+
with:
50+
node-version: '22.x'
51+
52+
- name: Check version bump
53+
if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed == 'true'
54+
run: |
55+
set -e
56+
PKG_VERSION=$(node -p "require('./package.json').version.replace(/^v/, '')")
57+
if [ -z "$PKG_VERSION" ]; then
58+
echo "::error::Could not read version from package.json"
59+
exit 1
60+
fi
61+
git fetch --tags --force 2>/dev/null || true
62+
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || true)
63+
if [ -z "$LATEST_TAG" ]; then
64+
echo "No existing tags found. Skipping version-bump check (first release)."
65+
exit 0
66+
fi
67+
LATEST_VERSION="${LATEST_TAG#v}"
68+
LATEST_VERSION="${LATEST_VERSION%%-*}"
69+
if [ "$(printf '%s\n' "$LATEST_VERSION" "$PKG_VERSION" | sort -V | tail -1)" != "$PKG_VERSION" ]; then
70+
echo "::error::Version bump required: package.json version ($PKG_VERSION) is not greater than latest tag ($LATEST_TAG). Please bump the version in package.json."
71+
exit 1
72+
fi
73+
if [ "$PKG_VERSION" = "$LATEST_VERSION" ]; then
74+
echo "::error::Version bump required: package.json version ($PKG_VERSION) equals latest tag ($LATEST_TAG). Please bump the version in package.json."
75+
exit 1
76+
fi
77+
CHANGELOG_VERSION=$(sed -nE 's/^## \[v?([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' CHANGELOG.md | head -1)
78+
if [ -z "$CHANGELOG_VERSION" ]; then
79+
echo "::error::Could not find a version entry in CHANGELOG.md (expected line like '## [v1.0.0](...)')."
80+
exit 1
81+
fi
82+
if [ "$CHANGELOG_VERSION" != "$PKG_VERSION" ]; then
83+
echo "::error::CHANGELOG version mismatch: CHANGELOG.md top version ($CHANGELOG_VERSION) does not match package.json version ($PKG_VERSION). Please add or update the CHANGELOG entry for $PKG_VERSION."
84+
exit 1
85+
fi
86+
echo "Version bump check passed: package.json and CHANGELOG.md are at $PKG_VERSION (latest tag: $LATEST_TAG)."

.github/workflows/unit-testing.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ on:
44
pull_request:
55
branches:
66
- development
7-
- staging
87
- master
98

109
jobs:

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# CHANGELOG
22

3+
## v2.7.0
4+
5+
### Jun 15, 2026
6+
- Enhancement: Endpoint integration
7+
38
## v2.6.0
49

510
### Feb 23, 2026

pom.xml

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<modelVersion>4.0.0</modelVersion>
66
<groupId>com.contentstack.sdk</groupId>
77
<artifactId>java</artifactId>
8-
<version>2.6.0</version>
8+
<version>2.7.0</version>
99
<packaging>jar</packaging>
1010
<name>contentstack-java</name>
1111
<description>Java SDK for Contentstack Content Delivery API</description>
@@ -462,6 +462,34 @@
462462
<artifactId>maven-jxr-plugin</artifactId>
463463
<version>2.3</version>
464464
</plugin>
465+
466+
<!--
467+
Refresh the bundled regions.json from the Contentstack artifact registry.
468+
Run whenever Contentstack adds new regions or service keys, then commit
469+
the updated src/main/resources/assets/regions.json.
470+
471+
Usage:
472+
mvn exec:exec@refresh-regions
473+
-->
474+
<plugin>
475+
<groupId>org.codehaus.mojo</groupId>
476+
<artifactId>exec-maven-plugin</artifactId>
477+
<version>3.1.0</version>
478+
<executions>
479+
<execution>
480+
<id>refresh-regions</id>
481+
<goals>
482+
<goal>exec</goal>
483+
</goals>
484+
<configuration>
485+
<executable>bash</executable>
486+
<arguments>
487+
<argument>${project.basedir}/scripts/download-regions.sh</argument>
488+
</arguments>
489+
</configuration>
490+
</execution>
491+
</executions>
492+
</plugin>
465493
</plugins>
466494
</build>
467495

scripts/download-regions.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env bash
2+
# Download the latest regions.json from the Contentstack artifacts registry and
3+
# write it to src/main/resources/assets/regions.json so it gets bundled into
4+
# the SDK jar on the next build.
5+
#
6+
# Usage:
7+
# ./scripts/download-regions.sh
8+
# mvn exec:exec@refresh-regions
9+
#
10+
# Run this whenever Contentstack announces new regions or service keys, then
11+
# commit the updated file:
12+
# git add src/main/resources/assets/regions.json
13+
# git commit -m "chore: refresh regions.json"
14+
15+
set -euo pipefail
16+
17+
REGIONS_URL="https://artifacts.contentstack.com/regions.json"
18+
DEST="$(dirname "$0")/../src/main/resources/assets/regions.json"
19+
DEST="$(cd "$(dirname "$DEST")" && pwd)/$(basename "$DEST")"
20+
21+
echo "Downloading regions.json from ${REGIONS_URL} ..."
22+
23+
if command -v curl &>/dev/null; then
24+
curl --silent --show-error --fail --location \
25+
--retry 3 --retry-delay 2 \
26+
-o "${DEST}" "${REGIONS_URL}"
27+
elif command -v wget &>/dev/null; then
28+
wget --quiet --tries=3 --waitretry=2 -O "${DEST}" "${REGIONS_URL}"
29+
else
30+
echo "Error: neither curl nor wget found. Install one and retry." >&2
31+
exit 1
32+
fi
33+
34+
# Validate the downloaded file contains a "regions" array
35+
if ! python3 -c "import sys, json; d=json.load(open('${DEST}')); assert 'regions' in d and len(d['regions']) > 0" 2>/dev/null &&
36+
! python -c "import sys, json; d=json.load(open('${DEST}')); assert 'regions' in d and len(d['regions']) > 0" 2>/dev/null; then
37+
# Fallback validation without Python — just check the key exists
38+
if ! grep -q '"regions"' "${DEST}"; then
39+
echo "Error: downloaded file does not look like a valid regions.json" >&2
40+
rm -f "${DEST}"
41+
exit 1
42+
fi
43+
fi
44+
45+
REGION_COUNT=$(grep -o '"id"' "${DEST}" | wc -l | tr -d ' ')
46+
echo "contentstack-java: regions.json updated (${REGION_COUNT} regions) → ${DEST}"

skills/dev-workflow/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ description: Use for Maven lifecycle, CI, JaCoCo, and branch expectations in con
1414

1515
### Branches
1616

17-
- Integration branches include **`development`**, **`staging`**, and **`master`**—confirm target branch for your PR against team policy.
17+
- Integration/release flow is **`development` -> `master`** (direct release PRs; no `staging` branch in release flow).
1818

1919
### Commands
2020

src/main/java/com/contentstack/sdk/Config.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class Config {
1818
protected String livePreviewContentType = null;
1919
protected String livePreviewEntryUid = null;
2020
protected String host = "cdn.contentstack.io";
21+
protected boolean hostOverridden = false;
2122
protected String version = "v3";
2223
protected String scheme = "https://";
2324
protected String endpoint;
@@ -167,6 +168,7 @@ public String getHost() {
167168
public void setHost(String hostName) {
168169
if (hostName != null && !hostName.isEmpty()) {
169170
host = hostName;
171+
hostOverridden = true;
170172
}
171173
}
172174

src/main/java/com/contentstack/sdk/Contentstack.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.contentstack.sdk;
22

3+
import java.util.Map;
34
import java.util.Objects;
45

56
/**
@@ -98,6 +99,60 @@ private static void validateCredentials(String stackApiKey, String deliveryToken
9899
}
99100
}
100101

102+
/**
103+
* Returns the Contentstack API URL for the given region and service.
104+
*
105+
* <p>Delegates to {@link Endpoint#getContentstackEndpoint(String, String)} — provided as a
106+
* convenience so callers can reach endpoint resolution through the same top-level class they
107+
* use to create stacks.
108+
*
109+
* @param region region ID or alias (e.g. {@code "na"}, {@code "eu"}, {@code "azure-na"})
110+
* @param service service key (e.g. {@code "contentDelivery"}, {@code "contentManagement"})
111+
* @return full URL including {@code https://} scheme
112+
* @throws IllegalArgumentException if the region or service is not recognised
113+
*/
114+
public static String getContentstackEndpoint(String region, String service) {
115+
return Endpoint.getContentstackEndpoint(region, service);
116+
}
117+
118+
/**
119+
* Returns the Contentstack API URL for the given region and service, optionally stripping
120+
* the {@code https://} scheme.
121+
*
122+
* @param region region ID or alias
123+
* @param service service key
124+
* @param omitHttps when {@code true}, returns the bare host without {@code https://}
125+
* @return URL or bare host
126+
* @throws IllegalArgumentException if the region or service is not recognised
127+
*/
128+
public static String getContentstackEndpoint(String region, String service, boolean omitHttps) {
129+
return Endpoint.getContentstackEndpoint(region, service, omitHttps);
130+
}
131+
132+
/**
133+
* Returns all service endpoints for the given region as an ordered map of service key to URL.
134+
*
135+
* @param region region ID or alias
136+
* @return map of service key → full URL
137+
* @throws IllegalArgumentException if the region is not recognised
138+
*/
139+
public static Map<String, String> getContentstackEndpoints(String region) {
140+
return Endpoint.getAllEndpoints(region);
141+
}
142+
143+
/**
144+
* Returns all service endpoints for the given region, optionally stripping the
145+
* {@code https://} scheme from every URL.
146+
*
147+
* @param region region ID or alias
148+
* @param omitHttps when {@code true}, returns bare hosts without {@code https://}
149+
* @return map of service key → URL or bare host
150+
* @throws IllegalArgumentException if the region is not recognised
151+
*/
152+
public static Map<String, String> getContentstackEndpoints(String region, boolean omitHttps) {
153+
return Endpoint.getAllEndpoints(region, omitHttps);
154+
}
155+
101156
private static Stack initializeStack(String stackApiKey, String deliveryToken, String environment, Config config) {
102157
Stack stack = new Stack(stackApiKey.trim());
103158
stack.setHeader("api_key", stackApiKey);

0 commit comments

Comments
 (0)