-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathversion_handler.js
More file actions
297 lines (259 loc) Β· 11.9 KB
/
Copy pathversion_handler.js
File metadata and controls
297 lines (259 loc) Β· 11.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const V2_REPO = 'Contentstack-Solutions/Red-Panda-Resort-v2';
async function createReleaseBranchInV2Repo(version) {
const branchName = `release/${version}`;
console.log(`\nCreating branch '${branchName}' in ${V2_REPO}...`);
try {
// Get the SHA of the default branch
const repoInfo = JSON.parse(
execSync(`gh api repos/${V2_REPO}`, { encoding: 'utf8', stdio: 'pipe' })
);
const defaultBranch = repoInfo.default_branch;
const refInfo = JSON.parse(
execSync(`gh api repos/${V2_REPO}/git/refs/heads/${defaultBranch}`, { encoding: 'utf8', stdio: 'pipe' })
);
const sha = refInfo.object.sha;
// Check if the branch already exists
try {
execSync(`gh api repos/${V2_REPO}/git/refs/heads/${branchName.replace('refs/heads/', '')}`, { encoding: 'utf8', stdio: 'pipe' });
console.log(`βοΈ Branch '${branchName}' already exists in ${V2_REPO}. Skipping...`);
return;
} catch {
// Branch doesn't exist, proceed
}
// Create the branch
execSync(
`gh api repos/${V2_REPO}/git/refs -f ref="refs/heads/${branchName}" -f sha="${sha}"`,
{ stdio: 'pipe' }
);
console.log(`β
Created branch '${branchName}' in ${V2_REPO}`);
} catch (error) {
console.error(`β Failed to create release branch in ${V2_REPO}: ${error.message}`);
console.error(' Make sure you are authenticated with: gh auth login');
}
}
/**
* Creates a version tag in the current git repository
* @param {string} customVersion - Optional custom version to use instead of package.json version
* @param {boolean} force - Force recreate tag if it already exists
*/
async function createVersionTag(customVersion = null, force = false) {
try {
let version;
if (customVersion) {
version = customVersion;
console.log(`Using custom version: ${version}`);
} else {
// Read package.json to get the current version
const packageJsonPath = path.join(__dirname, 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
version = packageJson.version;
}
console.log(`Creating version tag for ${version}...`);
// Check if we're in a git repository
try {
execSync('git status', { stdio: 'pipe' });
} catch (error) {
throw new Error('Not in a git repository');
}
// Check if the tag already exists locally
let tagExistsLocally = false;
try {
execSync(`git rev-parse ${version}`, { stdio: 'pipe' });
tagExistsLocally = true;
} catch (error) {
// Tag doesn't exist locally
}
// Check if the tag exists on remote
let tagExistsRemotely = false;
try {
const remoteOutput = execSync(`git ls-remote --tags origin ${version}`, { encoding: 'utf8', stdio: 'pipe' });
tagExistsRemotely = remoteOutput.trim().length > 0;
} catch (error) {
// Error checking remote or tag doesn't exist remotely
}
// Handle the three cases
if (tagExistsLocally && tagExistsRemotely) {
// Case 1: Tag exists both locally and remotely
if (force) {
console.log(`β οΈ Tag ${version} exists both locally and remotely. Force updating...`);
console.log(`ποΈ Deleting local tag ${version}...`);
try {
execSync(`git tag -d ${version}`, { stdio: 'inherit' });
console.log(`β
Local tag ${version} deleted`);
} catch (error) {
console.error(`β Failed to delete local tag: ${error.message}`);
throw error;
}
console.log(`ποΈ Deleting remote tag ${version}...`);
try {
execSync(`git push --delete origin ${version}`, { stdio: 'inherit' });
console.log(`β
Remote tag ${version} deleted`);
} catch (error) {
console.error(`β Failed to delete remote tag: ${error.message}`);
throw error;
}
} else {
console.log(`βοΈ Tag ${version} exists both locally and remotely. Skipping...`);
console.log(`π‘ Use force-update to update the existing tag`);
return;
}
} else if (tagExistsLocally && !tagExistsRemotely) {
// Case 2: Tag exists locally but not remotely - always proceed
console.log(`π Tag ${version} exists locally but not remotely. Deleting local tag and creating fresh...`);
try {
execSync(`git tag -d ${version}`, { stdio: 'inherit' });
console.log(`β
Local tag ${version} deleted`);
} catch (error) {
console.error(`β Failed to delete local tag: ${error.message}`);
throw error;
}
} else if (!tagExistsLocally && tagExistsRemotely) {
// Case 3: Tag exists remotely but not locally
if (force) {
console.log(`π Tag ${version} exists remotely but not locally. Force updating...`);
console.log(`ποΈ Deleting remote tag ${version}...`);
try {
execSync(`git push --delete origin ${version}`, { stdio: 'inherit' });
console.log(`β
Remote tag ${version} deleted`);
} catch (error) {
console.error(`β Failed to delete remote tag: ${error.message}`);
throw error;
}
} else {
console.log(`π Tag ${version} exists remotely but not locally.`);
console.log(`π‘ Use force-update to update the remote tag`);
return;
}
} else {
// Tag doesn't exist anywhere - proceed normally
console.log(`β¨ Tag ${version} doesn't exist. Creating new tag...`);
}
// Add all changes to staging
console.log('Adding changes to staging...');
execSync('git add .', { stdio: 'inherit' });
// Check if there are any changes to commit
try {
execSync('git diff --cached --quiet', { stdio: 'pipe' });
console.log('No changes to commit.');
} catch (error) {
// There are changes, commit them
console.log('Committing changes...');
execSync(`git commit -m "Release ${version}" --no-verify`, { stdio: 'inherit' });
}
// Create and push the tag
console.log(`Creating tag ${version}...`);
execSync(`git tag -a ${version} -m "Release version ${version}"`, { stdio: 'inherit' });
console.log(`Pushing tag ${version} to remote...`);
execSync(`git push origin ${version}`, { stdio: 'inherit' });
console.log(`β
Successfully created and pushed tag ${version}`);
// Create release branch in Red-Panda-Resort-v2 repo
await createReleaseBranchInV2Repo(version);
} catch (error) {
console.error('β Error creating version tag:', error.message);
process.exit(1);
}
}
/**
* Updates the version in package.json and creates a tag
* @param {string} type - Version bump type: 'patch', 'minor', or 'major'
*/
async function updateVersionAndTag(type = 'patch') {
try {
const packageJsonPath = path.join(__dirname, 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
// Parse current version
const versionParts = packageJson.version.split('.').map(Number);
let [major, minor, patch] = versionParts;
// Increment version based on type
switch (type) {
case 'major':
major++;
minor = 0;
patch = 0;
break;
case 'minor':
minor++;
patch = 0;
break;
case 'patch':
default:
patch++;
break;
}
const newVersion = `${major}.${minor}.${patch}`;
console.log(`Updating version from ${packageJson.version} to ${newVersion}`);
// Update package.json
packageJson.version = newVersion;
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
console.log(`Updated package.json version to ${newVersion}`);
// Create version tag
await createVersionTag();
} catch (error) {
console.error('β Error updating version and creating tag:', error.message);
process.exit(1);
}
}
// CLI interface
if (require.main === module) {
const args = process.argv.slice(2);
const command = ['major', 'minor', 'patch'].includes(args[0]) ? 'update' : args[0];
switch (command) {
case 'tag':
// Create a tag with custom version or current version
const customVersion = args[1];
const forceFlag = args.includes('force-update') || args.includes('-f');
if (customVersion && (customVersion === 'force-update')) {
// If first argument is force flag, use package.json version with force
createVersionTag(null, true);
} else {
// if (customVersion) {
// // Validate version format (basic semver check)
// const versionRegex = /^\d+\.\d+\.\d+(-[\w\.-]+)?$/;
// if (!versionRegex.test(customVersion)) {
// console.error('β Invalid version format. Please use semantic versioning (e.g., 1.0.0)');
// process.exit(1);
// }
// }
createVersionTag(customVersion, forceFlag);
}
break;
case 'update':
// Update version and create tag
const versionType = args[0] || 'patch';
if (!['patch', 'minor', 'major'].includes(versionType)) {
console.error('β Invalid version type. Use: patch, minor, or major');
process.exit(1);
}
updateVersionAndTag(versionType);
break;
default:
console.log(`
Usage:
npm run version:update tag [version] [force-update] - Create a tag using current or custom version
npm run version:update update [type] - Update version in package.json and create tag
Version types:
patch (default) - Increment patch version (x.x.X)
minor - Increment minor version (x.X.0)
major - Increment major version (X.0.0)
Flags:
force-update, -f - Force recreate tag if it already exists locally
Examples:
npm run version:update tag - Create tag ${require('./package.json').version} (current version)
npm run version:update tag 3.3.0 - Create tag v3.3.0 (custom version)
npm run version:update tag 3.3.0 force-update - Force recreate tag v3.3.0 (deletes local tag first)
npm run version:update tag force-update - Force recreate tag with current version
npm run version:update update - Update to next patch and create tag
npm run version:update update minor - Update to next minor and create tag
npm run version:update update major - Update to next major and create tag
Note: If you deleted a tag from GitHub but it still exists locally, use force-update flag
`);
break;
}
}
module.exports = {
createVersionTag,
updateVersionAndTag
};