forked from Anxcye/anx-reader
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrelease.dart
More file actions
353 lines (283 loc) · 9.64 KB
/
release.dart
File metadata and controls
353 lines (283 loc) · 9.64 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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
// ignore_for_file: avoid_print
import 'dart:io';
import 'dart:convert';
void main() async {
print('📦 Release Tool 📦');
// Check if git is initialized
if (!await _isGitRepo()) {
print('Error: Not a git repository.');
exit(1);
}
// Get current branch
final currentBranch = await _getCurrentBranch();
print('Current branch: $currentBranch');
if (currentBranch != 'develop') {
print('Error: Releases must start from develop branch.');
exit(1);
}
// Display release options
print('\nSelect release type:');
print('1. Stable (main branch)');
print('2. Beta (develop branch)');
print('3. Alpha (develop branch)');
final selection = stdin.readLineSync()?.trim();
String releaseType;
switch (selection) {
case '1':
releaseType = 'stable';
break;
case '2':
releaseType = 'beta';
break;
case '3':
releaseType = 'alpha';
break;
default:
print('Invalid selection.');
exit(1);
}
// Read pubspec.yaml
final pubspecFile = File('pubspec.yaml');
if (!pubspecFile.existsSync()) {
print('Error: pubspec.yaml not found.');
exit(1);
}
final pubspecContent = pubspecFile.readAsStringSync();
final versionRegExp = RegExp(r'version:\s*(\d+\.\d+\.\d+)(\+(\d+))?');
final match = versionRegExp.firstMatch(pubspecContent);
if (match == null) {
print('Error: Could not find version in pubspec.yaml.');
exit(1);
}
final currentVersion = match.group(1);
final currentBuild = match.group(3) != null ? int.parse(match.group(3)!) : 0;
print('\nCurrent version in pubspec.yaml: $currentVersion+$currentBuild');
// Get previous tags to suggest next version
final allTags = await _getAllTags();
final tags = await _getTagsByPattern(releaseType);
String suggestedVersion = currentVersion!;
// For pubspec build number - always increment
int suggestedPubspecBuild = currentBuild + 1;
// For tag build number - reset to 1 for each new version
int suggestedTagBuild = 1;
// For stable version, check if we need to increment the version
final stableTags = await _getTagsByPattern('stable');
final lastStableVersion = _extractLatestVersionFromTags(stableTags);
// If current version matches last stable version, suggest a new version
if (lastStableVersion == currentVersion) {
final parts = currentVersion.split('.');
if (parts.length == 3) {
final major = int.parse(parts[0]);
final minor = int.parse(parts[1]);
final patch = int.parse(parts[2]);
// Suggest incrementing the patch version
suggestedVersion = '$major.$minor.${patch + 1}';
print('\nCurrent version matches the latest stable release.');
print('Suggesting a new version: $suggestedVersion');
}
}
// For beta/alpha, check for existing builds of the current version in tags
final versionTags =
tags.where((tag) => tag.contains(suggestedVersion)).toList();
if (versionTags.isNotEmpty) {
suggestedTagBuild =
_findHighestBuildNumber(versionTags, suggestedVersion) + 1;
}
if (allTags.isNotEmpty) {
print('\nRecent tags:');
for (int i = 0; i < allTags.length && i < 5; i++) {
print(' ${allTags[i]}');
}
if (allTags.length > 5) {
print(' ... and ${allTags.length - 5} more');
}
}
if (tags.isNotEmpty) {
print('\nPrevious $releaseType tags:');
for (int i = 0; i < tags.length && i < 5; i++) {
print(' ${tags[i]}');
}
if (tags.length > 5) {
print(' ... and ${tags.length - 5} more');
}
}
String tagPrefix = '';
if (releaseType == 'stable') {
tagPrefix = 'v';
} else {
tagPrefix = '$releaseType-';
}
String suggestedTag = '';
if (releaseType == 'stable') {
suggestedTag = '$tagPrefix$suggestedVersion';
} else {
suggestedTag = '$tagPrefix$suggestedVersion-$suggestedTagBuild';
}
print('\nSuggested tag: $suggestedTag');
print('Suggested pubspec version: $suggestedVersion+$suggestedPubspecBuild');
print('\nEnter new version (or press Enter to use $suggestedVersion):');
final newVersion = stdin.readLineSync()?.trim();
final version =
newVersion?.isNotEmpty == true ? newVersion! : suggestedVersion;
print(
'Enter new pubspec build number (or press Enter to use $suggestedPubspecBuild):');
final newPubspecBuildStr = stdin.readLineSync()?.trim();
final pubspecBuild = newPubspecBuildStr?.isNotEmpty == true
? int.parse(newPubspecBuildStr!)
: suggestedPubspecBuild;
if (releaseType != 'stable') {
print(
'Enter new tag build number (or press Enter to use $suggestedTagBuild):');
final newTagBuildStr = stdin.readLineSync()?.trim();
suggestedTagBuild = newTagBuildStr?.isNotEmpty == true
? int.parse(newTagBuildStr!)
: suggestedTagBuild;
}
// Confirm new version
print('\nUpdate pubspec version to $version+$pubspecBuild? (y/n)');
final confirmVersion = stdin.readLineSync()?.trim().toLowerCase();
if (confirmVersion != 'y') {
print('Release cancelled.');
exit(0);
}
// Update pubspec.yaml
final updatedPubspec = pubspecContent.replaceFirst(
versionRegExp, 'version: $version+$pubspecBuild');
pubspecFile.writeAsStringSync(updatedPubspec);
print('Updated pubspec.yaml version to $version+$pubspecBuild');
// Create git tag
String tag = '';
if (releaseType == 'stable') {
tag = 'v$version';
} else {
tag = '$releaseType-$version-$suggestedTagBuild';
}
print('\nTag to create: $tag');
print('Confirm? (y/n)');
final confirmTag = stdin.readLineSync()?.trim().toLowerCase();
if (confirmTag != 'y') {
print('Tag creation cancelled. Version update is still applied.');
exit(0);
}
// Git operations
print('\nExecuting git commands...');
try {
// Add and commit changes in develop branch
await _runCommand('git', ['add', '.']);
print('✅ git add .');
await _runCommand('git', ['commit', '-m', 'release: $tag']);
print('✅ git commit -m "release: $tag"');
await _runCommand('git', ['push']);
print('✅ git push');
if (releaseType == 'stable') {
// For stable releases, merge to main, tag, then back to develop
print('\nMerging to main branch for stable release...');
await _runCommand('git', ['checkout', 'main']);
print('✅ Switched to main branch');
await _runCommand('git', ['merge', 'develop']);
print('✅ Merged develop into main');
await _runCommand('git', ['push']);
print('✅ Pushed main branch');
await _runCommand('git', ['tag', tag]);
print('✅ git tag $tag');
await _runCommand('git', ['push', 'origin', tag]);
print('✅ git push origin $tag');
// Return to develop branch
await _runCommand('git', ['checkout', 'develop']);
print('✅ Returned to develop branch');
print('\n🎉 Stable release $tag completed successfully!');
} else {
// For beta/alpha, just tag on develop
await _runCommand('git', ['tag', tag]);
print('✅ git tag $tag');
await _runCommand('git', ['push', 'origin', tag]);
print('✅ git push origin $tag');
print(
'\n🎉 ${releaseType.capitalize()} release $tag completed successfully!');
}
} catch (e) {
print('\n❌ Error during git operations: $e');
exit(1);
}
}
// Helper method to capitalize first letter
extension StringExtension on String {
String capitalize() {
return "${this[0].toUpperCase()}${substring(1)}";
}
}
Future<bool> _isGitRepo() async {
try {
final result =
await Process.run('git', ['rev-parse', '--is-inside-work-tree']);
return result.exitCode == 0 && result.stdout.toString().trim() == 'true';
} catch (e) {
return false;
}
}
Future<String> _getCurrentBranch() async {
final result =
await Process.run('git', ['rev-parse', '--abbrev-ref', 'HEAD']);
return result.stdout.toString().trim();
}
Future<List<String>> _getAllTags() async {
final result = await Process.run('git', ['tag', '--sort=-v:refname']);
if (result.exitCode != 0) {
return [];
}
final output = result.stdout.toString().trim();
if (output.isEmpty) {
return [];
}
return LineSplitter.split(output).toList();
}
Future<List<String>> _getTagsByPattern(String releaseType) async {
String pattern;
if (releaseType == 'stable') {
pattern = 'v[0-9]*';
} else {
pattern = '$releaseType-*';
}
final result =
await Process.run('git', ['tag', '-l', pattern, '--sort=-v:refname']);
if (result.exitCode != 0) {
return [];
}
final output = result.stdout.toString().trim();
if (output.isEmpty) {
return [];
}
return LineSplitter.split(output).toList();
}
String? _extractLatestVersionFromTags(List<String> tags) {
if (tags.isEmpty) return null;
// Extract version from a tag like v1.2.3
final versionRegExp = RegExp(r'v(\d+\.\d+\.\d+)');
final match = versionRegExp.firstMatch(tags.first);
return match?.group(1);
}
int _findHighestBuildNumber(List<String> tags, String version) {
int highestBuild = 0;
// For alpha/beta tags like alpha-1.2.3-5
final buildRegExp =
RegExp(r'(?:alpha|beta)-' + version.replaceAll('.', '\\.') + r'-(\d+)');
for (final tag in tags) {
final match = buildRegExp.firstMatch(tag);
if (match != null) {
final buildNum = int.parse(match.group(1)!);
if (buildNum > highestBuild) {
highestBuild = buildNum;
}
}
}
return highestBuild;
}
Future<ProcessResult> _runCommand(
String command, List<String> arguments) async {
final result = await Process.run(command, arguments);
if (result.exitCode != 0) {
final error = result.stderr.toString().trim();
throw Exception('Command failed: $command ${arguments.join(' ')}\n$error');
}
return result;
}