Skip to content

Commit ec38a86

Browse files
authored
chore: create change script (#1034)
1 parent 997957d commit ec38a86

3 files changed

Lines changed: 247 additions & 42 deletions

File tree

pubspec.lock

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,6 @@ packages:
99
url: "https://pub.dev"
1010
source: hosted
1111
version: "85.0.0"
12-
adaptive_number:
13-
dependency: transitive
14-
description:
15-
name: adaptive_number
16-
sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77"
17-
url: "https://pub.dev"
18-
source: hosted
19-
version: "1.0.0"
2012
analyzer:
2113
dependency: transitive
2214
description:
@@ -117,10 +109,10 @@ packages:
117109
dependency: transitive
118110
description:
119111
name: characters
120-
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
112+
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
121113
url: "https://pub.dev"
122114
source: hosted
123-
version: "1.4.0"
115+
version: "1.4.1"
124116
checked_yaml:
125117
dependency: transitive
126118
description:
@@ -197,10 +189,10 @@ packages:
197189
dependency: "direct main"
198190
description:
199191
name: dart_jsonwebtoken
200-
sha256: c6ecb3bb991c459b91c5adf9e871113dcb32bbe8fe7ca2c92723f88ffc1e0b7a
192+
sha256: cb79ed79baa02b4f59a597bf365873cbd83f9bb15273d63f7803802d21717c7d
201193
url: "https://pub.dev"
202194
source: hosted
203-
version: "3.3.2"
195+
version: "3.4.0"
204196
dart_style:
205197
dependency: transitive
206198
description:
@@ -213,10 +205,10 @@ packages:
213205
dependency: "direct main"
214206
description:
215207
name: dart_webrtc
216-
sha256: "4ed7b9fa9924e5a81eb39271e2c2356739dd1039d60a13b86ba6c5f448625086"
208+
sha256: "63a3ed6b958b5ba32d95ddba484c0dcf1cad4fd55eb551bb23c815b6f1e5d844"
217209
url: "https://pub.dev"
218210
source: hosted
219-
version: "1.7.0"
211+
version: "1.8.0"
220212
dbus:
221213
dependency: transitive
222214
description:
@@ -241,14 +233,6 @@ packages:
241233
url: "https://pub.dev"
242234
source: hosted
243235
version: "7.0.3"
244-
ed25519_edwards:
245-
dependency: transitive
246-
description:
247-
name: ed25519_edwards
248-
sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd"
249-
url: "https://pub.dev"
250-
source: hosted
251-
version: "0.3.1"
252236
fake_async:
253237
dependency: transitive
254238
description:
@@ -300,10 +284,10 @@ packages:
300284
dependency: "direct main"
301285
description:
302286
name: flutter_webrtc
303-
sha256: "0f86b518e9349e71a136a96e0ea11294cad8a8531b2bc9ae99e69df332ac898a"
287+
sha256: "8b220dc006c4891266735e516f7679bd08b7caaf7c36b1a93fb9357cec555f92"
304288
url: "https://pub.dev"
305289
source: hosted
306-
version: "1.3.0"
290+
version: "1.4.0"
307291
frontend_server_client:
308292
dependency: transitive
309293
description:
@@ -332,10 +316,10 @@ packages:
332316
dependency: transitive
333317
description:
334318
name: hooks
335-
sha256: "7a08a0d684cb3b8fb604b78455d5d352f502b68079f7b80b831c62220ab0a4f6"
319+
sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388
336320
url: "https://pub.dev"
337321
source: hosted
338-
version: "1.0.1"
322+
version: "1.0.2"
339323
http:
340324
dependency: "direct main"
341325
description:
@@ -436,10 +420,10 @@ packages:
436420
dependency: transitive
437421
description:
438422
name: logger
439-
sha256: a7967e31b703831a893bbc3c3dd11db08126fe5f369b5c648a36f821979f5be3
423+
sha256: "25aee487596a6257655a1e091ec2ae66bc30e7af663592cc3a27e6591e05035c"
440424
url: "https://pub.dev"
441425
source: hosted
442-
version: "2.6.2"
426+
version: "2.7.0"
443427
logging:
444428
dependency: "direct main"
445429
description:
@@ -452,18 +436,18 @@ packages:
452436
dependency: transitive
453437
description:
454438
name: matcher
455-
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
439+
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
456440
url: "https://pub.dev"
457441
source: hosted
458-
version: "0.12.17"
442+
version: "0.12.19"
459443
material_color_utilities:
460444
dependency: transitive
461445
description:
462446
name: material_color_utilities
463-
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
447+
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
464448
url: "https://pub.dev"
465449
source: hosted
466-
version: "0.11.1"
450+
version: "0.13.0"
467451
meta:
468452
dependency: "direct main"
469453
description:
@@ -500,10 +484,10 @@ packages:
500484
dependency: transitive
501485
description:
502486
name: native_toolchain_c
503-
sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
487+
sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572"
504488
url: "https://pub.dev"
505489
source: hosted
506-
version: "0.17.4"
490+
version: "0.17.6"
507491
nm:
508492
dependency: transitive
509493
description:
@@ -753,10 +737,10 @@ packages:
753737
dependency: transitive
754738
description:
755739
name: test_api
756-
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
740+
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
757741
url: "https://pub.dev"
758742
source: hosted
759-
version: "0.7.7"
743+
version: "0.7.10"
760744
timing:
761745
dependency: transitive
762746
description:
@@ -841,10 +825,10 @@ packages:
841825
dependency: transitive
842826
description:
843827
name: webrtc_interface
844-
sha256: ad0e5786b2acd3be72a3219ef1dde9e1cac071cf4604c685f11b61d63cdd6eb3
828+
sha256: c6f100eac5057d9a817a60473126f9828c796d42884d498af4f339c97b21014f
845829
url: "https://pub.dev"
846830
source: hosted
847-
version: "1.4.0"
831+
version: "1.5.1"
848832
win32:
849833
dependency: transitive
850834
description:

scripts/create_change.dart

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
#!/usr/bin/env dart
2+
/*
3+
* Copyright 2025 LiveKit
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import 'dart:io';
19+
20+
/// Creates a new change entry in the .changes directory.
21+
///
22+
/// Usage:
23+
/// dart scripts/create_change.dart
24+
/// dart scripts/create_change.dart --level patch --type fixed --name my-fix --description "Fix something"
25+
///
26+
/// Interactive mode (no args) will prompt for each field with arrow-key selection.
27+
/// CLI mode allows passing all fields as arguments.
28+
///
29+
/// Change file format:
30+
/// level type="kind" "description"
31+
///
32+
/// Examples:
33+
/// patch type="fixed" "Fix audio frame generation when publishing"
34+
/// minor type="added" "Add support for custom audio processing"
35+
/// major type="changed" "Breaking: Rename Room.connect() to Room.join()"
36+
37+
// ANSI escape codes
38+
const _esc = '\x1B[';
39+
const _reset = '\x1B[0m';
40+
const _bold = '\x1B[1m';
41+
const _dim = '\x1B[2m';
42+
const _green = '\x1B[32m';
43+
const _cyan = '\x1B[36m';
44+
const _hideCursor = '\x1B[?25l';
45+
const _showCursor = '\x1B[?25h';
46+
47+
const levels = ['patch', 'minor', 'major'];
48+
const levelDescriptions = {
49+
'patch': 'Bug fixes, no API changes',
50+
'minor': 'New features, backwards compatible',
51+
'major': 'Breaking changes',
52+
};
53+
54+
const types = [
55+
'added',
56+
'changed',
57+
'fixed',
58+
'refactor',
59+
'performance',
60+
'security',
61+
'deprecated',
62+
'removed',
63+
'docs',
64+
];
65+
66+
/// Read a single raw keypress (handles arrow keys as 3-byte escape sequences).
67+
List<int> readKey() {
68+
stdin.echoMode = false;
69+
stdin.lineMode = false;
70+
try {
71+
final first = stdin.readByteSync();
72+
if (first == 27) {
73+
// Escape sequence
74+
final second = stdin.readByteSync();
75+
if (second == 91) {
76+
final third = stdin.readByteSync();
77+
return [27, 91, third];
78+
}
79+
return [27, second];
80+
}
81+
return [first];
82+
} finally {
83+
stdin.lineMode = true;
84+
stdin.echoMode = true;
85+
}
86+
}
87+
88+
/// Interactive picker with arrow keys. Returns selected index.
89+
String pick(String label, List<String> options, {Map<String, String>? descriptions}) {
90+
var selected = 0;
91+
92+
void render() {
93+
stdout.write(_hideCursor);
94+
for (var i = 0; i < options.length; i++) {
95+
stdout.write('${_esc}2K'); // Clear entire line
96+
if (i == selected) {
97+
final desc = descriptions?[options[i]];
98+
final suffix = desc != null ? ' $_dim- $desc$_reset' : '';
99+
stdout.writeln(' $_cyan>$_reset $_bold${options[i]}$_reset$suffix');
100+
} else {
101+
stdout.writeln(' ${options[i]}');
102+
}
103+
}
104+
}
105+
106+
stdout.writeln('$_bold$_green?$_reset $_bold$label$_reset ${_dim}(arrow keys, enter to confirm)$_reset');
107+
render();
108+
109+
while (true) {
110+
final key = readKey();
111+
112+
if (key.length == 3 && key[0] == 27 && key[1] == 91) {
113+
if (key[2] == 65) {
114+
// Up arrow
115+
selected = (selected - 1) % options.length;
116+
if (selected < 0) selected = options.length - 1;
117+
} else if (key[2] == 66) {
118+
// Down arrow
119+
selected = (selected + 1) % options.length;
120+
}
121+
} else if (key.length == 1 && (key[0] == 10 || key[0] == 13)) {
122+
// Enter
123+
stdout.write(_showCursor);
124+
// Clear the list and show selection
125+
stdout.write('${_esc}${options.length}A'); // Move up
126+
stdout.write('${_esc}0J'); // Clear from cursor down
127+
stdout.writeln(' $_green>${_reset} $_bold${options[selected]}$_reset');
128+
return options[selected];
129+
} else if (key.length == 1 && (key[0] == 3 || key[0] == 27)) {
130+
// Ctrl+C or Escape
131+
stdout.write(_showCursor);
132+
exit(0);
133+
}
134+
135+
// Redraw
136+
stdout.write('${_esc}${options.length}A'); // Move up to start of list
137+
render();
138+
}
139+
}
140+
141+
/// Interactive text input with prompt.
142+
String input(String label) {
143+
stdout.write('$_bold$_green?$_reset $_bold$label$_reset ');
144+
final value = stdin.readLineSync()?.trim() ?? '';
145+
if (value.isEmpty) {
146+
stderr.writeln('Input required.');
147+
exit(1);
148+
}
149+
return value;
150+
}
151+
152+
String slugify(String text) {
153+
return text.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]+'), '-').replaceAll(RegExp(r'^-|-$'), '');
154+
}
155+
156+
void main(List<String> args) {
157+
final changesDir = Directory('.changes');
158+
if (!changesDir.existsSync()) {
159+
changesDir.createSync();
160+
}
161+
162+
String? level;
163+
String? type;
164+
String? name;
165+
String? description;
166+
167+
// Parse CLI args
168+
for (var i = 0; i < args.length; i++) {
169+
switch (args[i]) {
170+
case '--level':
171+
case '-l':
172+
level = args[++i];
173+
case '--type':
174+
case '-t':
175+
type = args[++i];
176+
case '--name':
177+
case '-n':
178+
name = args[++i];
179+
case '--description':
180+
case '-d':
181+
description = args[++i];
182+
case '--help':
183+
case '-h':
184+
stdout.writeln('Usage: dart scripts/create_change.dart [options]');
185+
stdout.writeln();
186+
stdout.writeln('Options:');
187+
stdout.writeln(' -l, --level Version bump level (${levels.join(', ')})');
188+
stdout.writeln(' -t, --type Change type (${types.join(', ')})');
189+
stdout.writeln(' -n, --name File name slug (e.g. fix-svc-dynacast)');
190+
stdout.writeln(' -d, --description Change description');
191+
stdout.writeln(' -h, --help Show this help');
192+
exit(0);
193+
}
194+
}
195+
196+
// Interactive prompts for missing fields
197+
level ??= pick('Level:', levels, descriptions: levelDescriptions);
198+
type ??= pick('Type:', types);
199+
name ??= slugify(input('Name (slug):'));
200+
description ??= input('Description:');
201+
202+
// Validate
203+
if (!levels.contains(level)) {
204+
stderr.writeln('Invalid level: $level');
205+
exit(1);
206+
}
207+
if (!types.contains(type)) {
208+
stderr.writeln('Invalid type: $type');
209+
exit(1);
210+
}
211+
212+
final content = '$level type="$type" "$description"\n';
213+
final file = File('.changes/$name');
214+
215+
if (file.existsSync()) {
216+
stderr.writeln('Change file already exists: .changes/$name');
217+
exit(1);
218+
}
219+
220+
file.writeAsStringSync(content);
221+
stdout.writeln();
222+
stdout.writeln('$_green\u2713$_reset Created $_bold.changes/$name$_reset');
223+
stdout.writeln(' ${_dim}${content.trim()}$_reset');
224+
}

0 commit comments

Comments
 (0)