@robylon/react-native-sdk 2.0.18 → 2.0.19-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -6
- package/scripts/create-branch.js +560 -0
- package/scripts/create-version-tag.js +29 -0
- package/scripts/get-next-version.js +29 -0
- package/scripts/husky-setup.js +32 -0
- package/scripts/prevent-direct-branch.js +37 -0
- package/scripts/publish-version.js +13 -0
- package/scripts/release.js +77 -0
- package/scripts/setup-git-hooks.js +18 -0
- package/scripts/update-version.js +15 -0
- package/scripts/validate-branch-name.sh +65 -0
- package/scripts/validate-publish.js +71 -0
- package/scripts/generate-version.js +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robylon/react-native-sdk",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.19-staging.1",
|
|
4
4
|
"description": "React Native SDK for Robylon",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"source": "src/index.tsx",
|
|
10
10
|
"readme": "README.md",
|
|
11
11
|
"scripts": {
|
|
12
|
-
"prebuild": "node scripts/
|
|
12
|
+
"prebuild": "node scripts/update-version.js",
|
|
13
13
|
"build": "bob build",
|
|
14
14
|
"clean": "rimraf lib",
|
|
15
15
|
"prebuild:dev": "cp .npmignore.development .npmignore",
|
|
@@ -22,8 +22,12 @@
|
|
|
22
22
|
"version:staging": "npm version prerelease --preid staging",
|
|
23
23
|
"version:production": "npm version patch",
|
|
24
24
|
"publish:dev": "npm run clean && npm run prebuild:dev && npm run version:dev && npm run build:dev && npm publish --tag dev",
|
|
25
|
-
"publish:staging": "
|
|
26
|
-
"publish:production": "
|
|
25
|
+
"publish:staging": "node scripts/validate-publish.js staging && npm version $(node scripts/get-next-version.js staging) && npm publish --tag staging",
|
|
26
|
+
"publish:production": "node scripts/validate-publish.js production && npm version $(node scripts/get-next-version.js production) && npm publish",
|
|
27
|
+
"publish:hotfix": "node scripts/validate-publish.js hotfix && npm version $(node scripts/get-next-version.js hotfix) && npm publish --tag hotfix",
|
|
28
|
+
"postinstall": "node scripts/setup-git-hooks.js",
|
|
29
|
+
"branch": "node scripts/create-branch.js",
|
|
30
|
+
"tag": "node scripts/create-version-tag.js"
|
|
27
31
|
},
|
|
28
32
|
"peerDependencies": {
|
|
29
33
|
"react": "*",
|
|
@@ -37,10 +41,12 @@
|
|
|
37
41
|
"babel-plugin-inline-dotenv": "^1.7.0",
|
|
38
42
|
"babel-plugin-transform-inline-environment-variables": "^0.4.4",
|
|
39
43
|
"env-cmd": "^10.1.0",
|
|
44
|
+
"husky": "^8.0.3",
|
|
40
45
|
"react": "^18.0.0",
|
|
41
|
-
"react-native": "^0.
|
|
46
|
+
"react-native": "^0.76.5",
|
|
42
47
|
"react-native-builder-bob": "^0.20.0",
|
|
43
48
|
"rimraf": "^6.0.1",
|
|
49
|
+
"semver": "^7.5.4",
|
|
44
50
|
"typescript": "^4.5.2"
|
|
45
51
|
},
|
|
46
52
|
"react-native-builder-bob": {
|
|
@@ -70,5 +76,10 @@
|
|
|
70
76
|
},
|
|
71
77
|
"author": "",
|
|
72
78
|
"license": "ISC",
|
|
73
|
-
"
|
|
79
|
+
"husky": {
|
|
80
|
+
"hooks": {
|
|
81
|
+
"commit-msg": "sh scripts/validate-branch-name.sh",
|
|
82
|
+
"post-checkout": "node scripts/prevent-direct-branch.js"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
74
85
|
}
|
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
const { execSync } = require("child_process");
|
|
2
|
+
const readline = require("readline");
|
|
3
|
+
const semver = require("semver");
|
|
4
|
+
|
|
5
|
+
const rl = readline.createInterface({
|
|
6
|
+
input: process.stdin,
|
|
7
|
+
output: process.stdout,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// Calculate the next patch version
|
|
11
|
+
const getNextPatchVersion = (currentVersion) => {
|
|
12
|
+
const versionParts = currentVersion.split(".");
|
|
13
|
+
const nextPatch = parseInt(versionParts[2]) + 1;
|
|
14
|
+
return `${versionParts[0]}.${versionParts[1]}.${nextPatch}`;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Validate branch name format
|
|
18
|
+
const isValidFeatureName = (name) => /^[a-z0-9-]+$/.test(name);
|
|
19
|
+
|
|
20
|
+
// Get the latest version from remote main
|
|
21
|
+
const getLatestMainVersion = () => {
|
|
22
|
+
try {
|
|
23
|
+
// Fetch latest from remote
|
|
24
|
+
execSync("git fetch origin main --quiet");
|
|
25
|
+
|
|
26
|
+
// Get package.json content from remote main
|
|
27
|
+
const remotePackageJson = execSync(
|
|
28
|
+
"git show origin/main:package.json"
|
|
29
|
+
).toString();
|
|
30
|
+
|
|
31
|
+
return JSON.parse(remotePackageJson).version;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
"Failed to get version from remote main. Ensure you have internet connection and repository access."
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Get target version for new branches
|
|
40
|
+
const getTargetVersion = () => {
|
|
41
|
+
try {
|
|
42
|
+
// Get current branch
|
|
43
|
+
const currentBranch = execSync("git rev-parse --abbrev-ref HEAD")
|
|
44
|
+
.toString()
|
|
45
|
+
.trim();
|
|
46
|
+
|
|
47
|
+
// If we're on a release branch, use its version for new features
|
|
48
|
+
if (currentBranch.startsWith("release/v")) {
|
|
49
|
+
return currentBranch.split("/v")[1];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Otherwise, use next version from main
|
|
53
|
+
const mainVersion = getLatestMainVersion();
|
|
54
|
+
return getNextPatchVersion(mainVersion);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new Error("Failed to determine target version");
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Validate that we're not creating conflicting branches
|
|
61
|
+
const validateVersionAvailability = async (targetVersion) => {
|
|
62
|
+
try {
|
|
63
|
+
// Get all remote branches
|
|
64
|
+
const remoteBranches = execSync("git ls-remote --heads origin")
|
|
65
|
+
.toString()
|
|
66
|
+
.split("\n")
|
|
67
|
+
.filter(Boolean)
|
|
68
|
+
.map((line) => line.split("/").slice(3).join("/"));
|
|
69
|
+
|
|
70
|
+
// Check for existing feature branches with same version
|
|
71
|
+
const conflictingBranches = remoteBranches.filter((branch) =>
|
|
72
|
+
branch.startsWith(`feature/v${targetVersion}/`)
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
if (conflictingBranches.length > 0) {
|
|
76
|
+
console.log(
|
|
77
|
+
"\n⚠️ Warning: The following branches already exist for version",
|
|
78
|
+
targetVersion
|
|
79
|
+
);
|
|
80
|
+
conflictingBranches.forEach((branch) => console.log(` - ${branch}`));
|
|
81
|
+
|
|
82
|
+
const proceed = await new Promise((resolve) =>
|
|
83
|
+
rl.question("\nDo you want to proceed anyway? (y/N): ", resolve)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (proceed.toLowerCase() !== "y") {
|
|
87
|
+
throw new Error("Operation cancelled by user");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (error.message === "Operation cancelled by user") {
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
throw new Error("Failed to validate version availability");
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Calculate the next version based on type
|
|
99
|
+
const getNextVersion = (currentVersion, type) => {
|
|
100
|
+
// Strip any pre-release tags (e.g., -staging.0) and split version
|
|
101
|
+
const baseVersion = currentVersion.split("-")[0];
|
|
102
|
+
const [major, minor, patch] = baseVersion.split(".").map(Number);
|
|
103
|
+
|
|
104
|
+
switch (type) {
|
|
105
|
+
case "major":
|
|
106
|
+
return `${major + 1}.0.0`;
|
|
107
|
+
case "minor":
|
|
108
|
+
return `${major}.${minor + 1}.0`;
|
|
109
|
+
case "patch":
|
|
110
|
+
return `${major}.${minor}.${patch + 1}`;
|
|
111
|
+
default:
|
|
112
|
+
throw new Error("Invalid version type");
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Get unmerged release branches
|
|
117
|
+
const getUnmergedReleaseBranches = () => {
|
|
118
|
+
try {
|
|
119
|
+
return execSync("git branch -r")
|
|
120
|
+
.toString()
|
|
121
|
+
.split("\n")
|
|
122
|
+
.filter((branch) => branch.includes("origin/release/v"))
|
|
123
|
+
.map((branch) => branch.trim())
|
|
124
|
+
.filter((branch) => {
|
|
125
|
+
const version = branch.split("/v")[1];
|
|
126
|
+
return semver.gt(version, getLatestMainVersion());
|
|
127
|
+
});
|
|
128
|
+
} catch (error) {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Get unmerged feature branches for a version
|
|
134
|
+
const getUnmergedFeatureBranches = (version) => {
|
|
135
|
+
return execSync("git branch -r")
|
|
136
|
+
.toString()
|
|
137
|
+
.split("\n")
|
|
138
|
+
.filter((branch) => branch.includes(`feature/v${version}/`))
|
|
139
|
+
.map((branch) => branch.trim());
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Get repository URL for PR creation
|
|
143
|
+
const getRepoUrl = () => {
|
|
144
|
+
const remoteUrl = execSync("git remote get-url origin").toString().trim();
|
|
145
|
+
return remoteUrl
|
|
146
|
+
.replace(/\.git$/, "")
|
|
147
|
+
.replace("git@github.com:", "https://github.com/");
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const createBranch = async () => {
|
|
151
|
+
try {
|
|
152
|
+
// Set environment variable to allow branch creation
|
|
153
|
+
process.env.BRANCH_CREATION_ALLOWED = "true";
|
|
154
|
+
|
|
155
|
+
// Ensure we have latest changes
|
|
156
|
+
console.log("\n📡 Fetching latest changes...");
|
|
157
|
+
execSync("git fetch origin --quiet");
|
|
158
|
+
|
|
159
|
+
// Get target version for new branches
|
|
160
|
+
const targetVersion = getTargetVersion();
|
|
161
|
+
const localVersion = require("../package.json").version;
|
|
162
|
+
const remoteMainVersion = getLatestMainVersion();
|
|
163
|
+
|
|
164
|
+
// Only check if we're on main
|
|
165
|
+
const currentBranch = execSync("git rev-parse --abbrev-ref HEAD")
|
|
166
|
+
.toString()
|
|
167
|
+
.trim();
|
|
168
|
+
if (currentBranch === "main") {
|
|
169
|
+
if (semver.lt(localVersion, remoteMainVersion)) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`Your local branch (${localVersion}) is behind remote main (${remoteMainVersion}). ` +
|
|
172
|
+
`Please pull latest changes first: git pull origin main`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
console.log("\nCurrent version:", localVersion);
|
|
178
|
+
|
|
179
|
+
console.log("\nSelect branch type:");
|
|
180
|
+
console.log("1. Feature branch");
|
|
181
|
+
console.log("2. Release branch");
|
|
182
|
+
console.log("3. Hotfix branch");
|
|
183
|
+
|
|
184
|
+
const answer = await new Promise((resolve) =>
|
|
185
|
+
rl.question("\nEnter your choice (1, 2 or 3): ", resolve)
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
if (answer === "1") {
|
|
189
|
+
// Feature branch
|
|
190
|
+
await validateVersionAvailability(targetVersion);
|
|
191
|
+
|
|
192
|
+
const featureName = await new Promise((resolve) =>
|
|
193
|
+
rl.question("\nEnter feature name (use-kebab-case): ", resolve)
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
if (!isValidFeatureName(featureName)) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
"Invalid feature name. Use kebab-case (e.g., 'add-chat-widget')"
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const branchName = `feature/v${targetVersion}/${featureName}`;
|
|
203
|
+
execSync(`git checkout -b ${branchName}`);
|
|
204
|
+
console.log(`\n✅ Successfully created feature branch: ${branchName}`);
|
|
205
|
+
} else if (answer === "2") {
|
|
206
|
+
// Release branch
|
|
207
|
+
console.log("\nCreating release branch for next version...");
|
|
208
|
+
|
|
209
|
+
const conflictedBranches = [];
|
|
210
|
+
|
|
211
|
+
// Check for unmerged release branches first
|
|
212
|
+
const unmergedReleases = getUnmergedReleaseBranches();
|
|
213
|
+
if (unmergedReleases.length > 0) {
|
|
214
|
+
console.log("\n⚠️ Unmerged release branches found:");
|
|
215
|
+
unmergedReleases.forEach((branch, index) =>
|
|
216
|
+
console.log(`${index + 1}. ${branch}`)
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const proceed = await new Promise((resolve) =>
|
|
220
|
+
rl.question(
|
|
221
|
+
"\nWould you like to:\n" +
|
|
222
|
+
"1. Add features to existing release\n" +
|
|
223
|
+
"2. Cancel operation\n" +
|
|
224
|
+
"\nEnter your choice (1 or 2): ",
|
|
225
|
+
resolve
|
|
226
|
+
)
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
if (proceed === "1") {
|
|
230
|
+
const releaseChoice = await new Promise((resolve) =>
|
|
231
|
+
rl.question("\nSelect release branch number: ", resolve)
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const selectedRelease = unmergedReleases[parseInt(releaseChoice) - 1];
|
|
235
|
+
if (!selectedRelease) {
|
|
236
|
+
throw new Error("Invalid release branch selection");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const releaseVersion = selectedRelease.split("/v")[1];
|
|
240
|
+
const unmergedFeatures = getUnmergedFeatureBranches(releaseVersion);
|
|
241
|
+
|
|
242
|
+
if (unmergedFeatures.length === 0) {
|
|
243
|
+
console.log(
|
|
244
|
+
"\nNo unmerged feature branches found for this version."
|
|
245
|
+
);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
console.log("\nUnmerged feature branches:");
|
|
250
|
+
unmergedFeatures.forEach((branch, index) =>
|
|
251
|
+
console.log(`${index + 1}. ${branch}`)
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
const featureChoice = await new Promise((resolve) =>
|
|
255
|
+
rl.question(
|
|
256
|
+
"\nSelect features to merge (comma-separated numbers or 'all'): ",
|
|
257
|
+
resolve
|
|
258
|
+
)
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
// Checkout the release branch
|
|
262
|
+
execSync(`git fetch origin ${selectedRelease}`);
|
|
263
|
+
execSync(
|
|
264
|
+
`git checkout -b ${selectedRelease.split("origin/")[1]} ${selectedRelease}`
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
// Merge selected features
|
|
268
|
+
const selectedFeatures =
|
|
269
|
+
featureChoice.toLowerCase() === "all"
|
|
270
|
+
? unmergedFeatures
|
|
271
|
+
: featureChoice
|
|
272
|
+
.split(",")
|
|
273
|
+
.map((num) => unmergedFeatures[parseInt(num.trim()) - 1])
|
|
274
|
+
.filter(Boolean);
|
|
275
|
+
|
|
276
|
+
for (const branch of selectedFeatures) {
|
|
277
|
+
console.log(`\nMerging ${branch}...`);
|
|
278
|
+
try {
|
|
279
|
+
execSync(
|
|
280
|
+
`git merge ${branch} --no-ff -m "Merge ${branch} into ${branchName}"`
|
|
281
|
+
);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
console.log(`\n⚠️ Merge conflict detected with ${branch}!`);
|
|
284
|
+
execSync("git merge --abort");
|
|
285
|
+
console.log(`\n⏩ Skipping ${branch} due to conflicts`);
|
|
286
|
+
conflictedBranches.push(branch);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// After all merges, show summary of conflicted branches
|
|
291
|
+
if (conflictedBranches.length > 0) {
|
|
292
|
+
const repoUrl = getRepoUrl();
|
|
293
|
+
console.log("\n🚨 MERGE CONFLICTS DETECTED 🚨");
|
|
294
|
+
console.log("=====================================");
|
|
295
|
+
conflictedBranches.forEach((branch) => {
|
|
296
|
+
const prUrl = `${repoUrl}/compare/${branchName}...${branch}?expand=1`;
|
|
297
|
+
console.log(`\n❌ ${branch}`);
|
|
298
|
+
console.log(`🔗 Create PR: ${prUrl}`);
|
|
299
|
+
});
|
|
300
|
+
console.log(
|
|
301
|
+
"\n⚠️ ACTION REQUIRED: Please resolve conflicts and merge these branches manually ⚠️"
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Show success message with merged/unmerged counts
|
|
306
|
+
const mergedCount =
|
|
307
|
+
selectedFeatures.length - conflictedBranches.length;
|
|
308
|
+
console.log(
|
|
309
|
+
`\n✅ Successfully merged ${mergedCount}/${selectedFeatures.length} branches`
|
|
310
|
+
);
|
|
311
|
+
if (conflictedBranches.length > 0) {
|
|
312
|
+
console.log(
|
|
313
|
+
`\n🚨 ATTENTION: ${conflictedBranches.length} branches require manual conflict resolution 🚨`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
throw new Error(
|
|
321
|
+
"Operation cancelled - please merge existing release first"
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Ask for version type
|
|
326
|
+
console.log("\nSelect version type:");
|
|
327
|
+
console.log(`1. Major (${getNextVersion(localVersion, "major")})`);
|
|
328
|
+
console.log(`2. Minor (${getNextVersion(localVersion, "minor")})`);
|
|
329
|
+
console.log(`3. Patch (${getNextVersion(localVersion, "patch")})`);
|
|
330
|
+
|
|
331
|
+
const versionType = await new Promise((resolve) =>
|
|
332
|
+
rl.question("\nEnter your choice (1, 2 or 3): ", resolve)
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
let releaseVersion;
|
|
336
|
+
switch (versionType) {
|
|
337
|
+
case "1":
|
|
338
|
+
releaseVersion = getNextVersion(localVersion, "major");
|
|
339
|
+
break;
|
|
340
|
+
case "2":
|
|
341
|
+
releaseVersion = getNextVersion(localVersion, "minor");
|
|
342
|
+
break;
|
|
343
|
+
case "3":
|
|
344
|
+
releaseVersion = getNextVersion(localVersion, "patch");
|
|
345
|
+
break;
|
|
346
|
+
default:
|
|
347
|
+
throw new Error("Invalid version type selection");
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
console.log(`\nUpdating package.json version to ${releaseVersion}...`);
|
|
351
|
+
|
|
352
|
+
// Create release branch
|
|
353
|
+
const branchName = `release/v${releaseVersion}`;
|
|
354
|
+
|
|
355
|
+
// Check if branch exists remotely
|
|
356
|
+
const remoteBranches = execSync("git ls-remote --heads origin")
|
|
357
|
+
.toString()
|
|
358
|
+
.split("\n")
|
|
359
|
+
.filter(Boolean)
|
|
360
|
+
.map((line) => line.split("/").slice(2).join("/"));
|
|
361
|
+
|
|
362
|
+
const branchExists = remoteBranches.includes(branchName);
|
|
363
|
+
|
|
364
|
+
if (branchExists) {
|
|
365
|
+
const proceed = await new Promise((resolve) =>
|
|
366
|
+
rl.question(
|
|
367
|
+
"\n⚠️ Release branch already exists remotely. Do you want to:\n" +
|
|
368
|
+
"1. Create new branch with force push (overwrite remote)\n" +
|
|
369
|
+
"2. Checkout existing branch and continue\n" +
|
|
370
|
+
"3. Cancel operation\n" +
|
|
371
|
+
"\nEnter your choice (1, 2 or 3): ",
|
|
372
|
+
resolve
|
|
373
|
+
)
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
switch (proceed) {
|
|
377
|
+
case "1":
|
|
378
|
+
// Continue with force push
|
|
379
|
+
break;
|
|
380
|
+
case "2":
|
|
381
|
+
execSync(`git fetch origin ${branchName}`);
|
|
382
|
+
execSync(`git checkout -b ${branchName} origin/${branchName}`);
|
|
383
|
+
console.log(`\n✅ Checked out existing branch: ${branchName}`);
|
|
384
|
+
return;
|
|
385
|
+
default:
|
|
386
|
+
throw new Error("Operation cancelled by user");
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
execSync(`git checkout -b ${branchName}`);
|
|
391
|
+
|
|
392
|
+
// Update version and commit
|
|
393
|
+
execSync(`npm version ${releaseVersion} --no-git-tag-version`);
|
|
394
|
+
execSync(`git add package.json package-lock.json`);
|
|
395
|
+
execSync(`git commit -m "chore: update version to ${releaseVersion}"`);
|
|
396
|
+
|
|
397
|
+
// Set up tracking and push to remote
|
|
398
|
+
try {
|
|
399
|
+
console.log("\n📡 Pushing to remote...");
|
|
400
|
+
execSync(
|
|
401
|
+
`git push -u origin ${branchName}${branchExists ? " --force" : ""}`
|
|
402
|
+
);
|
|
403
|
+
} catch (error) {
|
|
404
|
+
throw new Error(
|
|
405
|
+
`Failed to push to remote. Please check your permissions and try again.\nError: ${error.message}`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Get all feature branches for next version
|
|
410
|
+
const featureBranches = execSync("git branch -r")
|
|
411
|
+
.toString()
|
|
412
|
+
.split("\n")
|
|
413
|
+
.filter((branch) => branch.includes(`feature/v${releaseVersion}/`))
|
|
414
|
+
.map((branch) => branch.trim());
|
|
415
|
+
|
|
416
|
+
if (featureBranches.length === 0) {
|
|
417
|
+
console.log("\n⚠️ No feature branches found for this version.");
|
|
418
|
+
const proceed = await new Promise((resolve) =>
|
|
419
|
+
rl.question("\nContinue without feature branches? (y/N): ", resolve)
|
|
420
|
+
);
|
|
421
|
+
if (proceed.toLowerCase() !== "y") {
|
|
422
|
+
throw new Error("Operation cancelled by user");
|
|
423
|
+
}
|
|
424
|
+
} else {
|
|
425
|
+
console.log("\nAvailable feature branches:");
|
|
426
|
+
featureBranches.forEach((branch, index) =>
|
|
427
|
+
console.log(`${index + 1}. ${branch}`)
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
const featureChoice = await new Promise((resolve) =>
|
|
431
|
+
rl.question(
|
|
432
|
+
"\nSelect features to include (comma-separated numbers or 'all' or 'none'): ",
|
|
433
|
+
resolve
|
|
434
|
+
)
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
// Treat empty input as "none"
|
|
438
|
+
const choice = featureChoice.trim().toLowerCase();
|
|
439
|
+
const isNoneOrEmpty = !choice || choice === "none";
|
|
440
|
+
|
|
441
|
+
if (!isNoneOrEmpty) {
|
|
442
|
+
const selectedFeatures =
|
|
443
|
+
choice === "all"
|
|
444
|
+
? featureBranches
|
|
445
|
+
: choice
|
|
446
|
+
.split(",")
|
|
447
|
+
.map((num) => featureBranches[parseInt(num.trim()) - 1])
|
|
448
|
+
.filter(Boolean);
|
|
449
|
+
|
|
450
|
+
// Validate that we have valid selections
|
|
451
|
+
if (selectedFeatures.length === 0) {
|
|
452
|
+
throw new Error(
|
|
453
|
+
"No valid feature branches selected. Please try again"
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Merge selected feature branches
|
|
458
|
+
for (const branch of selectedFeatures) {
|
|
459
|
+
console.log(`\nMerging ${branch}...`);
|
|
460
|
+
try {
|
|
461
|
+
execSync(
|
|
462
|
+
`git merge ${branch} --no-ff -m "Merge ${branch} into ${branchName}"`
|
|
463
|
+
);
|
|
464
|
+
} catch (error) {
|
|
465
|
+
console.log(`\n⚠️ Merge conflict detected with ${branch}!`);
|
|
466
|
+
execSync("git merge --abort");
|
|
467
|
+
console.log(`\n⏩ Skipping ${branch} due to conflicts`);
|
|
468
|
+
conflictedBranches.push(branch);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// After all merges, show summary of conflicted branches
|
|
473
|
+
if (conflictedBranches.length > 0) {
|
|
474
|
+
const repoUrl = getRepoUrl();
|
|
475
|
+
console.log("\n🚨 MERGE CONFLICTS DETECTED 🚨");
|
|
476
|
+
console.log("=====================================");
|
|
477
|
+
conflictedBranches.forEach((branch) => {
|
|
478
|
+
const prUrl = `${repoUrl}/compare/${branchName}...${branch}?expand=1`;
|
|
479
|
+
console.log(`\n❌ ${branch}`);
|
|
480
|
+
console.log(`🔗 Create PR: ${prUrl}`);
|
|
481
|
+
});
|
|
482
|
+
console.log(
|
|
483
|
+
"\n⚠️ ACTION REQUIRED: Please resolve conflicts and merge these branches manually ⚠️"
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Show success message with merged/unmerged counts
|
|
488
|
+
const mergedCount =
|
|
489
|
+
selectedFeatures.length - conflictedBranches.length;
|
|
490
|
+
console.log(
|
|
491
|
+
`\n✅ Successfully merged ${mergedCount}/${selectedFeatures.length} branches`
|
|
492
|
+
);
|
|
493
|
+
if (conflictedBranches.length > 0) {
|
|
494
|
+
console.log(
|
|
495
|
+
`\n🚨 ATTENTION: ${conflictedBranches.length} branches require manual conflict resolution 🚨`
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
} else {
|
|
499
|
+
console.log("\n📝 Creating release branch without features");
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Push all changes to remote
|
|
504
|
+
try {
|
|
505
|
+
console.log("\n📡 Pushing final changes to remote...");
|
|
506
|
+
execSync(`git push origin ${branchName}`);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
throw new Error(
|
|
509
|
+
`Failed to push final changes. Please push manually.\nError: ${error.message}`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
console.log(`\n✅ Successfully created release branch: ${branchName}`);
|
|
514
|
+
console.log("\nNext steps:");
|
|
515
|
+
console.log("1. Review and test the changes");
|
|
516
|
+
console.log("2. Run 'npm run publish:production' when ready to release");
|
|
517
|
+
} else if (answer === "3") {
|
|
518
|
+
// Hotfix branch
|
|
519
|
+
console.log("\nEnter the version to hotfix (e.g., 1.1.14):");
|
|
520
|
+
const targetVersion = await new Promise((resolve) =>
|
|
521
|
+
rl.question("\nVersion: ", resolve)
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
// Validate version format
|
|
525
|
+
if (!/^\d+\.\d+\.\d+$/.test(targetVersion)) {
|
|
526
|
+
throw new Error("Invalid version format. Use semver (e.g., 1.1.14)");
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Get patch number
|
|
530
|
+
console.log("\nEnter patch number (e.g., 1 for first patch):");
|
|
531
|
+
const patchNumber = await new Promise((resolve) =>
|
|
532
|
+
rl.question("\nPatch number: ", resolve)
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
if (!/^\d+$/.test(patchNumber)) {
|
|
536
|
+
throw new Error("Invalid patch number. Use a number (e.g., 1)");
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const branchName = `hotfix/v${targetVersion}-patch.${patchNumber}`;
|
|
540
|
+
|
|
541
|
+
// Checkout the tag first
|
|
542
|
+
execSync(`git fetch --all --tags`);
|
|
543
|
+
execSync(`git checkout v${targetVersion}`);
|
|
544
|
+
execSync(`git checkout -b ${branchName}`);
|
|
545
|
+
|
|
546
|
+
console.log(`\n✅ Successfully created hotfix branch: ${branchName}`);
|
|
547
|
+
} else {
|
|
548
|
+
throw new Error("Invalid choice. Please select 1, 2 or 3.");
|
|
549
|
+
}
|
|
550
|
+
} catch (error) {
|
|
551
|
+
console.error("\n❌ Error:", error.message);
|
|
552
|
+
} finally {
|
|
553
|
+
// Clear the environment variable
|
|
554
|
+
delete process.env.BRANCH_CREATION_ALLOWED;
|
|
555
|
+
|
|
556
|
+
rl.close();
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
createBranch();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const { execSync } = require("child_process");
|
|
2
|
+
|
|
3
|
+
const createVersionTag = () => {
|
|
4
|
+
try {
|
|
5
|
+
const { version } = require("../package.json");
|
|
6
|
+
const tagName = `v${version}`;
|
|
7
|
+
|
|
8
|
+
// Check if tag exists
|
|
9
|
+
try {
|
|
10
|
+
execSync(`git tag -l "${tagName}"`).toString().trim();
|
|
11
|
+
console.log(`✅ Tag ${tagName} already exists, skipping tag creation`);
|
|
12
|
+
return;
|
|
13
|
+
} catch (error) {
|
|
14
|
+
// Tag doesn't exist, create it
|
|
15
|
+
try {
|
|
16
|
+
execSync(`git tag -a ${tagName} -m "Release: ${tagName}"`);
|
|
17
|
+
execSync(`git push origin ${tagName}`);
|
|
18
|
+
console.log(`✅ Successfully created and pushed tag: ${tagName}`);
|
|
19
|
+
} catch (tagError) {
|
|
20
|
+
throw new Error(`Failed to create or push tag: ${tagError.message}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error("\n❌ Error creating tag:", error.message);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
createVersionTag();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const semver = require("semver");
|
|
2
|
+
const { execSync } = require("child_process");
|
|
3
|
+
|
|
4
|
+
function getNextVersion(publishType) {
|
|
5
|
+
const { version } = require("../package.json");
|
|
6
|
+
|
|
7
|
+
switch (publishType) {
|
|
8
|
+
case "staging":
|
|
9
|
+
return semver.inc(version, "prerelease", "staging");
|
|
10
|
+
case "hotfix":
|
|
11
|
+
return semver.inc(version, "prerelease", "patch");
|
|
12
|
+
case "production":
|
|
13
|
+
return semver.inc(version, "patch");
|
|
14
|
+
default:
|
|
15
|
+
throw new Error(`Invalid publish type: ${publishType}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// If script is run directly
|
|
20
|
+
if (require.main === module) {
|
|
21
|
+
const publishType = process.argv[2];
|
|
22
|
+
if (!publishType) {
|
|
23
|
+
console.error("Please provide a publish type (staging|production|hotfix)");
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
console.log(getNextVersion(publishType));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = getNextVersion;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { execSync } = require("child_process");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
|
|
5
|
+
// Initialize husky
|
|
6
|
+
execSync("npx husky install");
|
|
7
|
+
|
|
8
|
+
// Create .husky directory if it doesn't exist
|
|
9
|
+
const huskyDir = path.join(__dirname, "../.husky");
|
|
10
|
+
if (!fs.existsSync(huskyDir)) {
|
|
11
|
+
fs.mkdirSync(huskyDir, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Create commit-msg hook
|
|
15
|
+
const hookContent = `#!/bin/sh
|
|
16
|
+
|
|
17
|
+
sh scripts/validate-branch-name.sh
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
fs.writeFileSync(path.join(huskyDir, "commit-msg"), hookContent);
|
|
21
|
+
fs.chmodSync(path.join(huskyDir, "commit-msg"), "755");
|
|
22
|
+
|
|
23
|
+
// Create post-checkout hook to prevent direct branch creation
|
|
24
|
+
const postCheckoutContent = `#!/bin/sh
|
|
25
|
+
|
|
26
|
+
node scripts/prevent-direct-branch.js
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
fs.writeFileSync(path.join(huskyDir, "post-checkout"), postCheckoutContent);
|
|
30
|
+
fs.chmodSync(path.join(huskyDir, "post-checkout"), "755");
|
|
31
|
+
|
|
32
|
+
console.log("Husky hooks installed successfully!");
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const { execSync } = require("child_process");
|
|
2
|
+
|
|
3
|
+
// Get command line arguments passed by Git
|
|
4
|
+
// In post-checkout hook, Git passes 3 arguments:
|
|
5
|
+
// 1. Previous HEAD ref
|
|
6
|
+
// 2. New HEAD ref
|
|
7
|
+
// 3. Flag (1 for branch checkout, 0 for file checkout)
|
|
8
|
+
const [prevHead, newHead, checkoutFlag] = process.argv.slice(2);
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
// Get the current branch name
|
|
12
|
+
const currentBranch = execSync("git rev-parse --abbrev-ref HEAD")
|
|
13
|
+
.toString()
|
|
14
|
+
.trim();
|
|
15
|
+
|
|
16
|
+
// Only check for branch creation (when prevHead and newHead are different and checkoutFlag is 1)
|
|
17
|
+
const isBranchCreation = prevHead !== newHead && checkoutFlag === "1";
|
|
18
|
+
|
|
19
|
+
// Check if this was a direct branch creation
|
|
20
|
+
if (
|
|
21
|
+
isBranchCreation && // Only check actual branch creation
|
|
22
|
+
!currentBranch.includes("temp-version-update")
|
|
23
|
+
) {
|
|
24
|
+
// If we moved from one branch to a new one, it was likely created directly
|
|
25
|
+
if (!process.env.BRANCH_CREATION_ALLOWED) {
|
|
26
|
+
console.error("\n❌ Direct branch creation is not allowed.");
|
|
27
|
+
console.error("Please use: npm run branch");
|
|
28
|
+
console.error(
|
|
29
|
+
"This ensures proper version control and naming conventions."
|
|
30
|
+
);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} catch (error) {
|
|
35
|
+
// If we can't determine, let it pass
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const { execSync } = require("child_process");
|
|
2
|
+
|
|
3
|
+
// Get current branch name
|
|
4
|
+
const branch = execSync("git rev-parse --abbrev-ref HEAD").toString().trim();
|
|
5
|
+
|
|
6
|
+
// If we're on a release branch, don't increment version
|
|
7
|
+
if (branch.startsWith("release/v")) {
|
|
8
|
+
console.log("On release branch - skipping version increment");
|
|
9
|
+
process.exit(0);
|
|
10
|
+
} else {
|
|
11
|
+
// For other branches, increment patch version
|
|
12
|
+
execSync("npm version patch", { stdio: "inherit" });
|
|
13
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const { execSync } = require("child_process");
|
|
2
|
+
const readline = require("readline");
|
|
3
|
+
|
|
4
|
+
const rl = readline.createInterface({
|
|
5
|
+
input: process.stdin,
|
|
6
|
+
output: process.stdout,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const release = async () => {
|
|
10
|
+
try {
|
|
11
|
+
// 1. Validate current branch is a release branch
|
|
12
|
+
const currentBranch = execSync("git rev-parse --abbrev-ref HEAD")
|
|
13
|
+
.toString()
|
|
14
|
+
.trim();
|
|
15
|
+
|
|
16
|
+
if (!currentBranch.startsWith("release/v")) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
"Release process must be started from a release branch.\n" +
|
|
19
|
+
`Current branch: ${currentBranch}\n` +
|
|
20
|
+
"Expected format: release/v{version}"
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 2. Check if version is already published
|
|
25
|
+
const version = currentBranch.split("/v")[1];
|
|
26
|
+
const publishedVersions = execSync(
|
|
27
|
+
"npm view @robylon/web-react-sdk versions --json"
|
|
28
|
+
).toString();
|
|
29
|
+
|
|
30
|
+
if (publishedVersions.includes(version)) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Version ${version} is already published to npm.\n` +
|
|
33
|
+
"Please create a new release branch with a different version."
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 3. Confirm release process
|
|
38
|
+
console.log("\n📦 Starting release process for version", version);
|
|
39
|
+
console.log("\nThis will:");
|
|
40
|
+
console.log("1. Publish to npm");
|
|
41
|
+
console.log("2. Create git tag");
|
|
42
|
+
console.log("3. Merge to main");
|
|
43
|
+
|
|
44
|
+
const proceed = await new Promise((resolve) =>
|
|
45
|
+
rl.question("\nProceed with release? (y/N): ", resolve)
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (proceed.toLowerCase() !== "y") {
|
|
49
|
+
throw new Error("Release cancelled by user");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 4. Run publish
|
|
53
|
+
console.log("\n🚀 Publishing to npm...");
|
|
54
|
+
execSync("npm run publish:production", { stdio: "inherit" });
|
|
55
|
+
|
|
56
|
+
// 5. Merge to main
|
|
57
|
+
console.log("\n🔄 Merging to main...");
|
|
58
|
+
execSync("git checkout main");
|
|
59
|
+
execSync("git pull origin main");
|
|
60
|
+
execSync(
|
|
61
|
+
`git merge ${currentBranch} --no-ff -m "chore: release version ${version}"`
|
|
62
|
+
);
|
|
63
|
+
execSync("git push origin main");
|
|
64
|
+
|
|
65
|
+
console.log("\n✅ Release completed successfully!");
|
|
66
|
+
console.log(`Version ${version} is now available on npm`);
|
|
67
|
+
console.log("Changes have been merged to main");
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error("\n❌ Release failed:");
|
|
70
|
+
console.error(error.message);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
} finally {
|
|
73
|
+
rl.close();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
release();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { execSync } = require("child_process");
|
|
4
|
+
|
|
5
|
+
// Create .git/hooks directory if it doesn't exist
|
|
6
|
+
const hooksDir = path.join(__dirname, "../.git/hooks");
|
|
7
|
+
if (!fs.existsSync(hooksDir)) {
|
|
8
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Copy the validate-branch-name script
|
|
12
|
+
const sourcePath = path.join(__dirname, "validate-branch-name.sh");
|
|
13
|
+
const targetPath = path.join(hooksDir, "commit-msg");
|
|
14
|
+
|
|
15
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
16
|
+
fs.chmodSync(targetPath, "755");
|
|
17
|
+
|
|
18
|
+
console.log("Git hooks installed successfully!");
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
// Read version from package.json
|
|
5
|
+
const packageJson = require("../package.json");
|
|
6
|
+
const version = packageJson.version;
|
|
7
|
+
|
|
8
|
+
// Create version.ts content
|
|
9
|
+
const content = `// This file is auto-generated. Do not modify it manually.
|
|
10
|
+
export const SDK_VERSION = '${version}';\n`;
|
|
11
|
+
|
|
12
|
+
// Write to version.ts
|
|
13
|
+
fs.writeFileSync(path.join(__dirname, "../src/version.ts"), content);
|
|
14
|
+
|
|
15
|
+
console.log(`Updated SDK_VERSION to ${version}`);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
echo "DEBUG: Script executed from: $0"
|
|
4
|
+
echo "DEBUG: PWD: $(pwd)"
|
|
5
|
+
|
|
6
|
+
# Get the full version including staging suffix if present
|
|
7
|
+
FULL_VERSION=$(node -p "require('./package.json').version")
|
|
8
|
+
|
|
9
|
+
# Check if this is a staging version before any other operations
|
|
10
|
+
if [[ $FULL_VERSION == *"-staging."* ]]; then
|
|
11
|
+
echo "Staging version detected. Skipping branch name validation."
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Get base version without pre-release tags (e.g., 1.1.14 from 1.1.14-staging.0)
|
|
16
|
+
BASE_VERSION=$(echo $FULL_VERSION | cut -d'-' -f1)
|
|
17
|
+
|
|
18
|
+
# Get the branch name
|
|
19
|
+
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
|
|
20
|
+
|
|
21
|
+
# Get latest published version from npm
|
|
22
|
+
LATEST_VERSION=$(npm view @robylon/web-react-sdk version 2>/dev/null || echo "0.0.0")
|
|
23
|
+
|
|
24
|
+
# Regular expression for valid branch names
|
|
25
|
+
FEATURE_PATTERN="^feature/v[0-9]+\.[0-9]+\.[0-9]+/[a-z0-9-]+$"
|
|
26
|
+
RELEASE_PATTERN="^release/v${BASE_VERSION}$"
|
|
27
|
+
HOTFIX_PATTERN="^hotfix/v[0-9]+\.[0-9]+\.[0-9]+(-patch\.[0-9]+)?$"
|
|
28
|
+
|
|
29
|
+
echo "Validating branch: $BRANCH_NAME"
|
|
30
|
+
echo "Current version: $BASE_VERSION"
|
|
31
|
+
echo "Latest published version: $LATEST_VERSION"
|
|
32
|
+
|
|
33
|
+
# For release branches, ensure version matches branch exactly
|
|
34
|
+
if [[ $BRANCH_NAME =~ ^release/v ]]; then
|
|
35
|
+
# Extract version from branch name
|
|
36
|
+
BRANCH_VERSION=$(echo $BRANCH_NAME | sed 's/release\/v//')
|
|
37
|
+
|
|
38
|
+
# Check if branch version matches package version
|
|
39
|
+
if [[ "$BRANCH_VERSION" != "$BASE_VERSION" ]]; then
|
|
40
|
+
echo "Error: Release branch version ($BRANCH_VERSION) must match package.json version ($BASE_VERSION)"
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# Check if trying to publish a version behind latest
|
|
45
|
+
if [[ $(node -p "require('semver').lt('$BASE_VERSION', '$LATEST_VERSION')") == "true" ]]; then
|
|
46
|
+
echo "Error: Cannot publish version $BASE_VERSION as it is behind the latest published version $LATEST_VERSION"
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
echo "✅ Valid release branch name and version"
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# Validate other branch types
|
|
55
|
+
if [[ $BRANCH_NAME =~ $FEATURE_PATTERN ]] || [[ $BRANCH_NAME =~ $HOTFIX_PATTERN ]]; then
|
|
56
|
+
echo "✅ Valid branch name"
|
|
57
|
+
exit 0
|
|
58
|
+
else
|
|
59
|
+
echo "Error: Invalid branch name format."
|
|
60
|
+
echo "Branch names must follow these formats:"
|
|
61
|
+
echo "- For feature branches: feature/v{version}/{feature-name}"
|
|
62
|
+
echo "- For release branches: release/v{version}"
|
|
63
|
+
echo "- For hotfix branches: hotfix/v{version}-patch.{number}"
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const { execSync } = require("child_process");
|
|
2
|
+
|
|
3
|
+
const validatePublish = (type) => {
|
|
4
|
+
try {
|
|
5
|
+
// Get current branch
|
|
6
|
+
const currentBranch = execSync("git rev-parse --abbrev-ref HEAD")
|
|
7
|
+
.toString()
|
|
8
|
+
.trim();
|
|
9
|
+
|
|
10
|
+
// Get version from package.json
|
|
11
|
+
const { version } = require("../package.json");
|
|
12
|
+
const baseVersion = version.split("-")[0];
|
|
13
|
+
|
|
14
|
+
switch (type) {
|
|
15
|
+
case "production":
|
|
16
|
+
if (!currentBranch.startsWith("release/v")) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
"Production publishing is only allowed from release branches.\n" +
|
|
19
|
+
"Current branch: " +
|
|
20
|
+
currentBranch +
|
|
21
|
+
"\n" +
|
|
22
|
+
"Expected format: release/v{version}"
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Verify release branch version matches package version
|
|
27
|
+
const releaseBranchVersion = currentBranch.split("/v")[1];
|
|
28
|
+
if (releaseBranchVersion !== baseVersion) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
"Release branch version does not match package.json version.\n" +
|
|
31
|
+
`Branch version: ${releaseBranchVersion}\n` +
|
|
32
|
+
`Package version: ${baseVersion}\n` +
|
|
33
|
+
"Please update package.json version to match the release branch"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
break;
|
|
37
|
+
|
|
38
|
+
case "hotfix":
|
|
39
|
+
if (!currentBranch.startsWith("hotfix/v")) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
"Hotfix publishing is only allowed from hotfix branches.\n" +
|
|
42
|
+
"Current branch: " +
|
|
43
|
+
currentBranch +
|
|
44
|
+
"\n" +
|
|
45
|
+
"Expected format: hotfix/v{version}-patch.{number}"
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
|
|
50
|
+
case "staging":
|
|
51
|
+
if (!currentBranch.startsWith("feature/v")) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
"Staging publishing is only allowed from feature branches.\n" +
|
|
54
|
+
"Current branch: " +
|
|
55
|
+
currentBranch +
|
|
56
|
+
"\n" +
|
|
57
|
+
"Expected format: feature/v{version}/{feature-name}"
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error("\n❌ Publish validation failed:");
|
|
64
|
+
console.error(error.message);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Get publish type from command line argument
|
|
70
|
+
const publishType = process.argv[2];
|
|
71
|
+
validatePublish(publishType);
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
const package = require("../package.json");
|
|
4
|
-
|
|
5
|
-
const content = `// This file is auto-generated. Do not modify it manually.
|
|
6
|
-
export const SDK_VERSION = '${package.version}';
|
|
7
|
-
`;
|
|
8
|
-
|
|
9
|
-
fs.writeFileSync(path.join(__dirname, "../src/version.ts"), content);
|