|
| 1 | +// Verify flutter_gen_core works against every supported major `xml` version. |
| 2 | +// |
| 3 | +// The package declares `xml: '>=6.0.0 <8.0.0'`, but a normal `dart pub get` |
| 4 | +// only ever resolves one major: `image` (a transitive dependency) pins `xml`, |
| 5 | +// so its newest releases force `xml` 7.x. As a result the normal `melos test` |
| 6 | +// never exercises the 6.x line. |
| 7 | +// |
| 8 | +// This script pins each major in turn via a temporary `pubspec_overrides.yaml` |
| 9 | +// (gitignored) and runs the core test suite against it, so a regression on |
| 10 | +// either xml 6.x or 7.x is caught. |
| 11 | +// |
| 12 | +// The 6.x pass also pins `image` below 4.9.0: `image` 4.9.0+ requires `xml` |
| 13 | +// ^7.0.1, so a downstream package constrained to `xml` 6.x resolves `image` |
| 14 | +// to its last 6.x-compatible release (4.8.0). Pinning only `xml` would instead |
| 15 | +// leave the newest `image` overridden onto an incompatible `xml` and fail to |
| 16 | +// compile, which is not a real resolution any consumer could hit. |
| 17 | +// |
| 18 | +// It is pure Dart (only `dart:io`) so it runs identically on Linux, macOS and |
| 19 | +// Windows. The xml 7.x pass requires Dart >= 3.11; on older toolchains it is |
| 20 | +// skipped with a warning instead of failing. |
| 21 | +// |
| 22 | +// Run via `melos test:xml-matrix` or `dart scripts/test_xml_versions.dart`. |
| 23 | + |
| 24 | +import 'dart:io'; |
| 25 | + |
| 26 | +Future<void> main() async { |
| 27 | + final coreDir = Directory('${_repoRoot().path}/packages/core'); |
| 28 | + final override = File('${coreDir.path}/pubspec_overrides.yaml'); |
| 29 | + |
| 30 | + // Always remove the temporary override and re-resolve so the working tree |
| 31 | + // and lockfile are left in their default state, even if a pass fails. |
| 32 | + void cleanup() { |
| 33 | + if (override.existsSync()) override.deleteSync(); |
| 34 | + Process.runSync( |
| 35 | + Platform.resolvedExecutable, |
| 36 | + ['pub', 'get'], |
| 37 | + workingDirectory: coreDir.path, |
| 38 | + ); |
| 39 | + } |
| 40 | + |
| 41 | + try { |
| 42 | + // image 4.9.0+ requires xml ^7.0.1, so the xml 6.x pass must also pin |
| 43 | + // image to its last 6.x-compatible release. This mirrors what pub resolves |
| 44 | + // for a downstream package constrained to xml 6.x. |
| 45 | + await _runPass( |
| 46 | + '^6.0.0', |
| 47 | + coreDir, |
| 48 | + override, |
| 49 | + extraOverrides: {'image': "'>=4.5.4 <4.9.0'"}, |
| 50 | + ); |
| 51 | + |
| 52 | + final dart = _dartVersion(); |
| 53 | + if (dart.major > 3 || (dart.major == 3 && dart.minor >= 11)) { |
| 54 | + await _runPass('^7.0.0', coreDir, override); |
| 55 | + } else { |
| 56 | + stdout.writeln('\n⚠️ Skipping xml 7.x pass: requires Dart >= 3.11 ' |
| 57 | + '(found ${dart.major}.${dart.minor}).'); |
| 58 | + } |
| 59 | + } catch (error) { |
| 60 | + cleanup(); |
| 61 | + stderr.writeln('\n❌ $error'); |
| 62 | + exit(1); |
| 63 | + } |
| 64 | + |
| 65 | + cleanup(); |
| 66 | + stdout |
| 67 | + .writeln('\n✅ flutter_gen_core passed against all checked xml versions.'); |
| 68 | +} |
| 69 | + |
| 70 | +Future<void> _runPass( |
| 71 | + String constraint, |
| 72 | + Directory coreDir, |
| 73 | + File override, { |
| 74 | + Map<String, String> extraOverrides = const {}, |
| 75 | +}) async { |
| 76 | + stdout.writeln('\n========================================================'); |
| 77 | + stdout.writeln(' flutter_gen_core test pass — pinning xml: $constraint'); |
| 78 | + stdout.writeln('========================================================'); |
| 79 | + final overrides = StringBuffer('dependency_overrides:\n xml: $constraint\n'); |
| 80 | + extraOverrides.forEach((name, c) => overrides.writeln(' $name: $c')); |
| 81 | + override.writeAsStringSync(overrides.toString()); |
| 82 | + await _dart(['pub', 'get'], coreDir); |
| 83 | + stdout.writeln('-> resolved xml ${_resolvedXmlVersion(coreDir)}'); |
| 84 | + await _dart(['test'], coreDir); |
| 85 | +} |
| 86 | + |
| 87 | +/// Runs `dart <args>` in [cwd], streaming output, and throws on failure. |
| 88 | +Future<void> _dart(List<String> args, Directory cwd) async { |
| 89 | + final process = await Process.start( |
| 90 | + Platform.resolvedExecutable, |
| 91 | + args, |
| 92 | + workingDirectory: cwd.path, |
| 93 | + mode: ProcessStartMode.inheritStdio, |
| 94 | + ); |
| 95 | + final code = await process.exitCode; |
| 96 | + if (code != 0) { |
| 97 | + throw 'dart ${args.join(' ')} failed (exit $code) in ${cwd.path}'; |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +/// Parses the running Dart SDK version, e.g. "3.11.3 (stable) (...)". |
| 102 | +({int major, int minor}) _dartVersion() { |
| 103 | + final parts = Platform.version.split(' ').first.split('.'); |
| 104 | + return (major: int.parse(parts[0]), minor: int.parse(parts[1])); |
| 105 | +} |
| 106 | + |
| 107 | +/// Reads the resolved `xml` version out of `packages/core/pubspec.lock`. |
| 108 | +String _resolvedXmlVersion(Directory coreDir) { |
| 109 | + final lock = File('${coreDir.path}/pubspec.lock'); |
| 110 | + if (!lock.existsSync()) return 'unknown'; |
| 111 | + final lines = lock.readAsLinesSync(); |
| 112 | + for (var i = 0; i < lines.length; i++) { |
| 113 | + if (lines[i].trimRight() == ' xml:') { |
| 114 | + for (var j = i + 1; |
| 115 | + j < lines.length && lines[j].startsWith(' '); |
| 116 | + j++) { |
| 117 | + final match = RegExp(r'version:\s*"?([^"\s]+)"?').firstMatch(lines[j]); |
| 118 | + if (match != null) return match.group(1)!; |
| 119 | + } |
| 120 | + } |
| 121 | + } |
| 122 | + return 'unknown'; |
| 123 | +} |
| 124 | + |
| 125 | +/// Locates the melos workspace root by walking up from this script's location, |
| 126 | +/// falling back to the current directory (melos runs scripts from the root). |
| 127 | +Directory _repoRoot() { |
| 128 | + var dir = File.fromUri(Platform.script).parent; |
| 129 | + while (dir.path != dir.parent.path) { |
| 130 | + if (File('${dir.path}/melos.yaml').existsSync()) return dir; |
| 131 | + dir = dir.parent; |
| 132 | + } |
| 133 | + return Directory.current; |
| 134 | +} |
0 commit comments