From ef4f4aa4d1a50faa4d2216a90ed5a6236bba32c0 Mon Sep 17 00:00:00 2001 From: Victor Carreras <34163765+vicajilau@users.noreply.github.com> Date: Fri, 19 Dec 2025 08:44:24 +0100 Subject: [PATCH 1/5] ci: Dynamically fetch Flutter versions for the CI test matrix instead of using a hardcoded list. --- .github/workflows/main.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 841120e8..fe35b91b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,12 +7,24 @@ on: branches: [ master ] jobs: + setup_flutter_versions: + runs-on: ubuntu-latest + outputs: + flutter-versions: ${{ steps.id_flutter_versions.outputs.versions }} + steps: + - name: Fetch Flutter versions + id: id_flutter_versions + run: | + VERSIONS=$(curl -s https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json | jq -c '[.releases | map(select(.channel == "stable")) | .[:5] | map(.version)] | flatten + ["stable", "beta"]') + echo "versions=$VERSIONS" >> $GITHUB_OUTPUT + test_flutter_versions: + needs: setup_flutter_versions runs-on: ubuntu-latest strategy: fail-fast: false matrix: - flutter: [ '3.24.0', '3.27.0', '3.29.0', '3.32.0', '3.35.0', 'stable', 'beta' ] + flutter: ${{ fromJson(needs.setup_flutter_versions.outputs.flutter-versions) }} name: Tests on Flutter ${{ matrix.flutter }} steps: - uses: actions/checkout@v4 From 6def2e9a22a83b285d5331edad95858e30ce7411 Mon Sep 17 00:00:00 2001 From: Victor Carreras <34163765+vicajilau@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:17:34 +0100 Subject: [PATCH 2/5] ci: update Flutter version fetching to select 5 most recent unique major.minor stable releases. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fe35b91b..213e6e63 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,7 @@ jobs: - name: Fetch Flutter versions id: id_flutter_versions run: | - VERSIONS=$(curl -s https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json | jq -c '[.releases | map(select(.channel == "stable")) | .[:5] | map(.version)] | flatten + ["stable", "beta"]') + VERSIONS=$(curl -s https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json | jq -c '[.releases | map(select(.channel == "stable")) | group_by(.version | split(".")[:2] | join(".")) | map(.[0]) | sort_by(.release_date) | reverse | .[:5] | map(.version)] | flatten + ["stable", "beta"]') echo "versions=$VERSIONS" >> $GITHUB_OUTPUT test_flutter_versions: From 462cf8f8f50484241a87e7e97ac4c35095b75e5d Mon Sep 17 00:00:00 2001 From: Victor Carreras <34163765+vicajilau@users.noreply.github.com> Date: Fri, 19 Dec 2025 13:46:08 +0100 Subject: [PATCH 3/5] feat: Add Dart script to fetch Flutter versions, include tests, and integrate into CI workflow. --- .github/workflows/main.yml | 6 +-- test/fetch_versions_test.dart | 69 +++++++++++++++++++++++++++++++++ tool/fetch_versions.dart | 73 +++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 test/fetch_versions_test.dart create mode 100644 tool/fetch_versions.dart diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 213e6e63..e99044de 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,11 +12,11 @@ jobs: outputs: flutter-versions: ${{ steps.id_flutter_versions.outputs.versions }} steps: + - uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1 - name: Fetch Flutter versions id: id_flutter_versions - run: | - VERSIONS=$(curl -s https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json | jq -c '[.releases | map(select(.channel == "stable")) | group_by(.version | split(".")[:2] | join(".")) | map(.[0]) | sort_by(.release_date) | reverse | .[:5] | map(.version)] | flatten + ["stable", "beta"]') - echo "versions=$VERSIONS" >> $GITHUB_OUTPUT + run: dart tool/fetch_versions.dart >> $GITHUB_OUTPUT test_flutter_versions: needs: setup_flutter_versions diff --git a/test/fetch_versions_test.dart b/test/fetch_versions_test.dart new file mode 100644 index 00000000..3a9eab86 --- /dev/null +++ b/test/fetch_versions_test.dart @@ -0,0 +1,69 @@ +import 'package:flutter_test/flutter_test.dart'; +import '../tool/fetch_versions.dart'; + +void main() { + test('parseVersions extracts last 4 minor versions + stable/beta', () { + final json = { + "releases": [ + // Minor 3.38 + { + "version": "3.38.5", + "channel": "stable", + "release_date": "2025-12-12T10:00:00Z" + }, + { + "version": "3.38.4", + "channel": "stable", + "release_date": "2025-12-11T10:00:00Z" + }, + + // Minor 3.35 + { + "version": "3.35.7", + "channel": "stable", + "release_date": "2025-11-12T10:00:00Z" + }, + + // Minor 3.32 + { + "version": "3.32.8", + "channel": "stable", + "release_date": "2025-10-12T10:00:00Z" + }, + + // Minor 3.29 + { + "version": "3.29.3", + "channel": "stable", + "release_date": "2025-09-12T10:00:00Z" + }, + + // Minor 3.27 (5th older minor, should be excluded if limit is 4) + { + "version": "3.27.4", + "channel": "stable", + "release_date": "2025-08-12T10:00:00Z" + }, + + // Beta (ignored by filter, but 'beta' string is added manually) + { + "version": "3.40.0-beta", + "channel": "beta", + "release_date": "2025-12-13T10:00:00Z" + }, + ] + }; + + final versions = parseVersions(json); + + expect(versions, [ + "3.38.5", // Latest of 3.38 + "3.35.7", // Latest of 3.35 + "3.32.8", // Latest of 3.32 + "3.29.3", // Latest of 3.29 + "stable", + "beta" + ]); + expect(versions.length, 6); + }); +} diff --git a/tool/fetch_versions.dart b/tool/fetch_versions.dart new file mode 100644 index 00000000..199f189e --- /dev/null +++ b/tool/fetch_versions.dart @@ -0,0 +1,73 @@ +import 'dart:convert'; +import 'dart:io'; + +Future main() async { + try { + final json = await _fetchJson(); + final versions = parseVersions(json); + print('versions=${jsonEncode(versions)}'); + } catch (e) { + stderr.writeln('Error: $e'); + exit(1); + } +} + +Future> _fetchJson() async { + final url = Uri.parse( + 'https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json'); + final httpClient = HttpClient(); + try { + final request = await httpClient.getUrl(url); + final response = await request.close(); + if (response.statusCode != 200) { + throw HttpException('Failed to fetch releases: ${response.statusCode}'); + } + final responseBody = await response.transform(utf8.decoder).join(); + return jsonDecode(responseBody) as Map; + } finally { + httpClient.close(); + } +} + +List parseVersions(Map json) { + final releases = (json['releases'] as List).cast>(); + + // Filter for stable channel + final stableReleases = releases.where((r) => r['channel'] == 'stable'); + + // Group by Major.Minor (e.g. 3.38) + final Map> latestByMinor = {}; + + for (final release in stableReleases) { + final version = release['version'] as String; + final parts = version.split('.'); + if (parts.length < 2) continue; + final minor = '${parts[0]}.${parts[1]}'; + + final releaseDate = DateTime.parse(release['release_date']); + + if (!latestByMinor.containsKey(minor)) { + latestByMinor[minor] = release; + } else { + final currentBest = latestByMinor[minor]!; + final currentBestDate = DateTime.parse(currentBest['release_date']); + // Keep the most recent patch + if (releaseDate.isAfter(currentBestDate)) { + latestByMinor[minor] = release; + } + } + } + + // Sort groups by release date descending + final sortedByDate = latestByMinor.values.toList() + ..sort((a, b) { + final dateA = DateTime.parse(a['release_date']); + final dateB = DateTime.parse(b['release_date']); + return dateB.compareTo(dateA); + }); + + // Take the last 4 minor versions + final top4 = sortedByDate.take(4).map((r) => r['version'] as String).toList(); + + return [...top4, 'stable', 'beta']; +} From 0863e61a1364ae17496b59f4f94f662f1dbb533a Mon Sep 17 00:00:00 2001 From: Victor Carreras <34163765+vicajilau@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:10:23 +0100 Subject: [PATCH 4/5] feat: Update `parseVersions` to include 5 historical minor versions by skipping the latest from the top 6, instead of 4. --- test/fetch_versions_test.dart | 25 ++++++++++++++++++------- tool/fetch_versions.dart | 10 +++++++--- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/test/fetch_versions_test.dart b/test/fetch_versions_test.dart index 3a9eab86..20292d6f 100644 --- a/test/fetch_versions_test.dart +++ b/test/fetch_versions_test.dart @@ -2,7 +2,9 @@ import 'package:flutter_test/flutter_test.dart'; import '../tool/fetch_versions.dart'; void main() { - test('parseVersions extracts last 4 minor versions + stable/beta', () { + test( + 'parseVersions extracts last 6 minor versions, skips 1st, returns 5 + stable/beta', + () { final json = { "releases": [ // Minor 3.38 @@ -38,13 +40,20 @@ void main() { "release_date": "2025-09-12T10:00:00Z" }, - // Minor 3.27 (5th older minor, should be excluded if limit is 4) + // Minor 3.27 { "version": "3.27.4", "channel": "stable", "release_date": "2025-08-12T10:00:00Z" }, + // Minor 3.24 + { + "version": "3.24.2", + "channel": "stable", + "release_date": "2025-07-12T10:00:00Z" + }, + // Beta (ignored by filter, but 'beta' string is added manually) { "version": "3.40.0-beta", @@ -57,13 +66,15 @@ void main() { final versions = parseVersions(json); expect(versions, [ - "3.38.5", // Latest of 3.38 - "3.35.7", // Latest of 3.35 - "3.32.8", // Latest of 3.32 - "3.29.3", // Latest of 3.29 + // "3.38.5", // Skipped + "3.35.7", + "3.32.8", + "3.29.3", + "3.27.4", + "3.24.2", "stable", "beta" ]); - expect(versions.length, 6); + expect(versions.length, 7); }); } diff --git a/tool/fetch_versions.dart b/tool/fetch_versions.dart index 199f189e..266f8ce3 100644 --- a/tool/fetch_versions.dart +++ b/tool/fetch_versions.dart @@ -66,8 +66,12 @@ List parseVersions(Map json) { return dateB.compareTo(dateA); }); - // Take the last 4 minor versions - final top4 = sortedByDate.take(4).map((r) => r['version'] as String).toList(); + // Take the last 6 minor versions + final top6 = sortedByDate.take(6).map((r) => r['version'] as String).toList(); - return [...top4, 'stable', 'beta']; + // Skip the first one (latest stable) to avoid duplication with 'stable' channel + // Return the next 5 + final historicVersions = top6.skip(1).toList(); + + return [...historicVersions, 'stable', 'beta']; } From a31d370f9a6a3e981a9c68ada5eccc8541902c42 Mon Sep 17 00:00:00 2001 From: Victor Carreras <34163765+vicajilau@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:13:48 +0100 Subject: [PATCH 5/5] fix: reduce the number of returned historic minor versions from 5 to 4 --- test/fetch_versions_test.dart | 6 +++--- tool/fetch_versions.dart | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/fetch_versions_test.dart b/test/fetch_versions_test.dart index 20292d6f..0195885d 100644 --- a/test/fetch_versions_test.dart +++ b/test/fetch_versions_test.dart @@ -3,7 +3,7 @@ import '../tool/fetch_versions.dart'; void main() { test( - 'parseVersions extracts last 6 minor versions, skips 1st, returns 5 + stable/beta', + 'parseVersions extracts last 5 minor versions, skips 1st, returns 4 + stable/beta', () { final json = { "releases": [ @@ -71,10 +71,10 @@ void main() { "3.32.8", "3.29.3", "3.27.4", - "3.24.2", + // "3.24.2", // Removed "stable", "beta" ]); - expect(versions.length, 7); + expect(versions.length, 6); }); } diff --git a/tool/fetch_versions.dart b/tool/fetch_versions.dart index 266f8ce3..a4f1a586 100644 --- a/tool/fetch_versions.dart +++ b/tool/fetch_versions.dart @@ -66,12 +66,12 @@ List parseVersions(Map json) { return dateB.compareTo(dateA); }); - // Take the last 6 minor versions - final top6 = sortedByDate.take(6).map((r) => r['version'] as String).toList(); + // Take the last 5 minor versions + final top5 = sortedByDate.take(5).map((r) => r['version'] as String).toList(); // Skip the first one (latest stable) to avoid duplication with 'stable' channel - // Return the next 5 - final historicVersions = top6.skip(1).toList(); + // Return the next 4 + final historicVersions = top5.skip(1).toList(); return [...historicVersions, 'stable', 'beta']; }