From bc6907b940610224910331a5f72b66083e034e3f Mon Sep 17 00:00:00 2001 From: Pavel Akhrameev Date: Fri, 5 Jun 2026 12:26:15 +0400 Subject: [PATCH 1/2] fix: add xml 7.x support Widen flutter_gen_core's `xml` constraint from `^6.0.0` to `>=6.0.0 <8.0.0`, so projects depending on both flutter_gen and `xml: ^7.0.0` can resolve. Replace the deprecated `XmlNode.text` with `XmlNode.innerText` (present since xml 6.0.0, so compatible with both majors). Add a cross-platform Dart matrix (scripts/test_xml_versions.dart, exposed as `melos test:xml-matrix`) plus a CI step that runs flutter_gen_core's tests against both xml 6.x and 7.x via a temporary dependency_overrides, so neither major regresses. Closes #761 --- .github/workflows/build.yml | 4 + melos.yaml | 4 + .../core/lib/generators/colors_generator.dart | 3 +- packages/core/pubspec.yaml | 2 +- scripts/test_xml_versions.dart | 117 ++++++++++++++++++ 5 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 scripts/test_xml_versions.dart diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9037ef35c..36c7fe0d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,6 +44,10 @@ jobs: if: startsWith(matrix.os, 'ubuntu') run: melos test:dart --no-select + - name: Test flutter_gen_core against both xml majors (6.x and 7.x) + if: startsWith(matrix.os, 'ubuntu') + run: melos test:xml-matrix + - name: Run tests for Flutter packages if: startsWith(matrix.os, 'ubuntu') run: melos test:flutter --no-select diff --git a/melos.yaml b/melos.yaml index 7e262c157..af410db08 100644 --- a/melos.yaml +++ b/melos.yaml @@ -122,6 +122,10 @@ scripts: bash ./scripts/coverage.sh packages/core description: bash ./scripts/coverage.sh packages/core + test:xml-matrix: + run: dart scripts/test_xml_versions.dart + description: Run flutter_gen_core tests against both supported xml majors (6.x and 7.x) + publish:force: run: dart pub publish -f exec: diff --git a/packages/core/lib/generators/colors_generator.dart b/packages/core/lib/generators/colors_generator.dart index 3d022eee2..6aaeb884d 100644 --- a/packages/core/lib/generators/colors_generator.dart +++ b/packages/core/lib/generators/colors_generator.dart @@ -106,8 +106,7 @@ class _Color { _Color.fromXmlElement(XmlElement element) : this( element.getAttribute('name')!, - // ignore: deprecated_member_use - element.text, + element.innerText, element.getAttribute('type')?.split(' ') ?? List.empty(), ); diff --git a/packages/core/pubspec.yaml b/packages/core/pubspec.yaml index a2371c304..34cbc8c6c 100644 --- a/packages/core/pubspec.yaml +++ b/packages/core/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: path: ^1.8.0 yaml: ^3.0.0 mime: '>=1.0.0 <3.0.0' - xml: ^6.0.0 + xml: '>=6.0.0 <8.0.0' dartx: ^1.0.0 color: ^3.0.0 collection: ^1.15.0 diff --git a/scripts/test_xml_versions.dart b/scripts/test_xml_versions.dart new file mode 100644 index 000000000..aa957cecc --- /dev/null +++ b/scripts/test_xml_versions.dart @@ -0,0 +1,117 @@ +// Verify flutter_gen_core works against every supported major `xml` version. +// +// The package declares `xml: '>=6.0.0 <8.0.0'`, but `dart pub get` is +// conservative and resolves to the 6.x line by default (because the package +// still supports older SDKs, while xml 7.x requires Dart >= 3.11). As a result +// the normal `melos test` only ever exercises one of the two majors. +// +// This script pins each major in turn via a temporary `pubspec_overrides.yaml` +// (gitignored) and runs the core test suite against it, so a regression on +// either xml 6.x or 7.x is caught. +// +// It is pure Dart (only `dart:io`) so it runs identically on Linux, macOS and +// Windows. The xml 7.x pass requires Dart >= 3.11; on older toolchains it is +// skipped with a warning instead of failing. +// +// Run via `melos test:xml-matrix` or `dart scripts/test_xml_versions.dart`. + +import 'dart:io'; + +Future main() async { + final coreDir = Directory('${_repoRoot().path}/packages/core'); + final override = File('${coreDir.path}/pubspec_overrides.yaml'); + + // Always remove the temporary override and re-resolve so the working tree + // and lockfile are left in their default state, even if a pass fails. + void cleanup() { + if (override.existsSync()) override.deleteSync(); + Process.runSync( + Platform.resolvedExecutable, + ['pub', 'get'], + workingDirectory: coreDir.path, + ); + } + + try { + await _runPass('^6.0.0', coreDir, override); + + final dart = _dartVersion(); + if (dart.major > 3 || (dart.major == 3 && dart.minor >= 11)) { + await _runPass('^7.0.0', coreDir, override); + } else { + stdout.writeln('\n⚠️ Skipping xml 7.x pass: requires Dart >= 3.11 ' + '(found ${dart.major}.${dart.minor}).'); + } + } catch (error) { + cleanup(); + stderr.writeln('\n❌ $error'); + exit(1); + } + + cleanup(); + stdout + .writeln('\n✅ flutter_gen_core passed against all checked xml versions.'); +} + +Future _runPass( + String constraint, + Directory coreDir, + File override, +) async { + stdout.writeln('\n========================================================'); + stdout.writeln(' flutter_gen_core test pass — pinning xml: $constraint'); + stdout.writeln('========================================================'); + override.writeAsStringSync('dependency_overrides:\n xml: $constraint\n'); + await _dart(['pub', 'get'], coreDir); + stdout.writeln('-> resolved xml ${_resolvedXmlVersion(coreDir)}'); + await _dart(['test'], coreDir); +} + +/// Runs `dart ` in [cwd], streaming output, and throws on failure. +Future _dart(List args, Directory cwd) async { + final process = await Process.start( + Platform.resolvedExecutable, + args, + workingDirectory: cwd.path, + mode: ProcessStartMode.inheritStdio, + ); + final code = await process.exitCode; + if (code != 0) { + throw 'dart ${args.join(' ')} failed (exit $code) in ${cwd.path}'; + } +} + +/// Parses the running Dart SDK version, e.g. "3.11.3 (stable) (...)". +({int major, int minor}) _dartVersion() { + final parts = Platform.version.split(' ').first.split('.'); + return (major: int.parse(parts[0]), minor: int.parse(parts[1])); +} + +/// Reads the resolved `xml` version out of `packages/core/pubspec.lock`. +String _resolvedXmlVersion(Directory coreDir) { + final lock = File('${coreDir.path}/pubspec.lock'); + if (!lock.existsSync()) return 'unknown'; + final lines = lock.readAsLinesSync(); + for (var i = 0; i < lines.length; i++) { + if (lines[i].trimRight() == ' xml:') { + for (var j = i + 1; + j < lines.length && lines[j].startsWith(' '); + j++) { + final match = RegExp(r'version:\s*"?([^"\s]+)"?').firstMatch(lines[j]); + if (match != null) return match.group(1)!; + } + } + } + return 'unknown'; +} + +/// Locates the melos workspace root by walking up from this script's location, +/// falling back to the current directory (melos runs scripts from the root). +Directory _repoRoot() { + var dir = File.fromUri(Platform.script).parent; + while (dir.path != dir.parent.path) { + if (File('${dir.path}/melos.yaml').existsSync()) return dir; + dir = dir.parent; + } + return Directory.current; +} From ee80d45e5e3d3b0e7e985016217903f09464a7ae Mon Sep 17 00:00:00 2001 From: Pavel Akhrameev Date: Mon, 8 Jun 2026 17:21:00 +0400 Subject: [PATCH 2/2] fix tests: pin image below 4.9.0 in xml 6.x matrix pass --- scripts/test_xml_versions.dart | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/scripts/test_xml_versions.dart b/scripts/test_xml_versions.dart index aa957cecc..8b7c71488 100644 --- a/scripts/test_xml_versions.dart +++ b/scripts/test_xml_versions.dart @@ -1,14 +1,20 @@ // Verify flutter_gen_core works against every supported major `xml` version. // -// The package declares `xml: '>=6.0.0 <8.0.0'`, but `dart pub get` is -// conservative and resolves to the 6.x line by default (because the package -// still supports older SDKs, while xml 7.x requires Dart >= 3.11). As a result -// the normal `melos test` only ever exercises one of the two majors. +// The package declares `xml: '>=6.0.0 <8.0.0'`, but a normal `dart pub get` +// only ever resolves one major: `image` (a transitive dependency) pins `xml`, +// so its newest releases force `xml` 7.x. As a result the normal `melos test` +// never exercises the 6.x line. // // This script pins each major in turn via a temporary `pubspec_overrides.yaml` // (gitignored) and runs the core test suite against it, so a regression on // either xml 6.x or 7.x is caught. // +// The 6.x pass also pins `image` below 4.9.0: `image` 4.9.0+ requires `xml` +// ^7.0.1, so a downstream package constrained to `xml` 6.x resolves `image` +// to its last 6.x-compatible release (4.8.0). Pinning only `xml` would instead +// leave the newest `image` overridden onto an incompatible `xml` and fail to +// compile, which is not a real resolution any consumer could hit. +// // It is pure Dart (only `dart:io`) so it runs identically on Linux, macOS and // Windows. The xml 7.x pass requires Dart >= 3.11; on older toolchains it is // skipped with a warning instead of failing. @@ -33,7 +39,15 @@ Future main() async { } try { - await _runPass('^6.0.0', coreDir, override); + // image 4.9.0+ requires xml ^7.0.1, so the xml 6.x pass must also pin + // image to its last 6.x-compatible release. This mirrors what pub resolves + // for a downstream package constrained to xml 6.x. + await _runPass( + '^6.0.0', + coreDir, + override, + extraOverrides: {'image': "'>=4.5.4 <4.9.0'"}, + ); final dart = _dartVersion(); if (dart.major > 3 || (dart.major == 3 && dart.minor >= 11)) { @@ -56,12 +70,15 @@ Future main() async { Future _runPass( String constraint, Directory coreDir, - File override, -) async { + File override, { + Map extraOverrides = const {}, +}) async { stdout.writeln('\n========================================================'); stdout.writeln(' flutter_gen_core test pass — pinning xml: $constraint'); stdout.writeln('========================================================'); - override.writeAsStringSync('dependency_overrides:\n xml: $constraint\n'); + final overrides = StringBuffer('dependency_overrides:\n xml: $constraint\n'); + extraOverrides.forEach((name, c) => overrides.writeln(' $name: $c')); + override.writeAsStringSync(overrides.toString()); await _dart(['pub', 'get'], coreDir); stdout.writeln('-> resolved xml ${_resolvedXmlVersion(coreDir)}'); await _dart(['test'], coreDir);