Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sites/docs/src/content/release/release-notes/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ outlined in the [Beta channel][] section of the [SDK archive][] page.
## Stable releases

* 3.44.0
* [3.44.0 announcement][]
* [3.44.0 release notes & change log][]
* [3.44.0 breaking changes & migrations][]
* 3.41.0
Expand Down Expand Up @@ -122,6 +123,7 @@ outlined in the [Beta channel][] section of the [SDK archive][] page.
* Earlier
* [Archived release notes][]

[3.44.0 announcement]: {{site.flutter-blog}}/whats-new-in-flutter-3-44-b0cc1ad3c527
[3.44.0 release notes & change log]: /release/release-notes/release-notes-3.44.0
[3.44.0 breaking changes & migrations]: /release/breaking-changes#released-in-flutter-3-44
[3.41.0 announcement]: {{site.flutter-blog}}/whats-new-in-flutter-3-41-302ec140e632
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ This page has release notes for Flutter 3.44.
* [Dot shorthands] Migrate examples/api/test by @loic-sharma in [183966](https://github.com/flutter/flutter/pull/183966)
* Warn about the use of TestSemantics by @justinmc in [184369](https://github.com/flutter/flutter/pull/184369)
* [CP-beta]Fix an ordering dependency in the services/system_chrome_test.dart test suite by @flutteractionsbot in [185104](https://github.com/flutter/flutter/pull/185104)
* [CP-beta] Update Flutter Android templates to AGP 9 by @jesswrd in [186099](https://github.com/flutter/flutter/pull/186099)

### Material

Expand Down Expand Up @@ -366,6 +367,8 @@ This page has release notes for Flutter 3.44.
* [cupertino.dart] Implement CupertinoMenuAnchor and CupertinoMenuItem using RawMenuAnchor by @davidhicks980 in [182036](https://github.com/flutter/flutter/pull/182036)
* Remove editable_text_utils cross-imports from material and cupertino … by @xfce0 in [184519](https://github.com/flutter/flutter/pull/184519)
* Add more error handling to unawaited callsites by @victorsanni in [184526](https://github.com/flutter/flutter/pull/184526)
* [CP-beta] Enable the `avoid_final_parameters` lint. (#185216) by @walley892 in [186199](https://github.com/flutter/flutter/pull/186199)
* Revert "[CP-beta] Enable the `avoid_final_parameters` lint. (#185216)" by @walley892 in [186270](https://github.com/flutter/flutter/pull/186270)

### iOS

Expand Down Expand Up @@ -400,6 +403,7 @@ This page has release notes for Flutter 3.44.
* [CP-beta]Fix killing wrong xcrun command by @flutteractionsbot in [185097](https://github.com/flutter/flutter/pull/185097)
* [CP-beta]Only use LLDB breakpoint in debug mode by @flutteractionsbot in [185343](https://github.com/flutter/flutter/pull/185343)
* [CP-beta][SwiftPM] Enable package resolution on xcodebuild commands by @flutteractionsbot in [185746](https://github.com/flutter/flutter/pull/185746)
* [CP-beta]Match on process name before killing for SwiftPM by @flutteractionsbot in [185856](https://github.com/flutter/flutter/pull/185856)

### Android

Expand Down Expand Up @@ -452,6 +456,7 @@ This page has release notes for Flutter 3.44.
* Reland "Apply rect clipping to surface views" by @gmackall in [184732](https://github.com/flutter/flutter/pull/184732)
* Fix Android engine flags defaulting to true for malformed values by @realmeylisdev in [184631](https://github.com/flutter/flutter/pull/184631)
* [CP-beta][Android] Gboard Text Shift Stuck Fix by @flutteractionsbot in [185749](https://github.com/flutter/flutter/pull/185749)
* [CP-beta]Fix Broken Flutter Docs Link by @flutteractionsbot in [186106](https://github.com/flutter/flutter/pull/186106)

### Windows

Expand All @@ -463,6 +468,7 @@ This page has release notes for Flutter 3.44.
* [Windows] Restore and enable IAccessibleEx implementation by @loic-peron-inetum-public in [175406](https://github.com/flutter/flutter/pull/175406)
* Windows: Get graphics adapter from engine instead of view by @knopp in [184479](https://github.com/flutter/flutter/pull/184479)
* [CP-beta][Win32] FlutterDesktopEngineGetGraphicsAdapter should use out parameter by @flutteractionsbot in [185634](https://github.com/flutter/flutter/pull/185634)
* [CP-beta] Fix pointer position by @loic-sharma in [186034](https://github.com/flutter/flutter/pull/186034)

### Linux

Expand Down Expand Up @@ -639,7 +645,7 @@ This page has release notes for Flutter 3.44.
* Improve error message when `dart-define` content are not `base64 encoded` and add more test cases by @AbdeMohlbi in [184219](https://github.com/flutter/flutter/pull/184219)
* [ Widget Preview ] Use analysis server for widget preview detection by @bkonyi in [184473](https://github.com/flutter/flutter/pull/184473)
* [data_assets] Cleanup tests by @dcharkes in [184209](https://github.com/flutter/flutter/pull/184209)
* Enable SwiftPM by default on Stable by @okorohelijah in [184495](https://github.com/flutter/flutter/pull/184495)
* Enable SPM by default on Stable by @okorohelijah in [184495](https://github.com/flutter/flutter/pull/184495)
* [ Widget Preview ] Handle collections and records in custom preview annotations by @bkonyi in [184518](https://github.com/flutter/flutter/pull/184518)
* forward an application name to DDS by @jakemac53 in [184459](https://github.com/flutter/flutter/pull/184459)
* Reverts "[data_assets] Cleanup tests (#184209)" by @auto-submit[bot] in [184575](https://github.com/flutter/flutter/pull/184575)
Expand All @@ -656,6 +662,8 @@ This page has release notes for Flutter 3.44.
* [CP-beta]Use relative path for reloadedSourcesUri and reloaded modules by @flutteractionsbot in [185540](https://github.com/flutter/flutter/pull/185540)
* [CP-beta][Widget Preview] Fix flaky integration test timeout during flutter clean (#184991) by @bkonyi in [185300](https://github.com/flutter/flutter/pull/185300)
* [CP-beta]Fix `--enable-hcpp` flag being ignored in release by @flutteractionsbot in [185717](https://github.com/flutter/flutter/pull/185717)
* [CP-beta] Update iOS tools to fat binaries (#185868) by @vashworth in [186063](https://github.com/flutter/flutter/pull/186063)
* [CP-beta] [AGP 9] Update AGP Error With Flutter Docs (#185043) by @jesswrd in [186040](https://github.com/flutter/flutter/pull/186040)

### Documentation

Expand Down Expand Up @@ -938,6 +946,10 @@ This page has release notes for Flutter 3.44.
* [CP-beta]Fix sdfs being enabled for MacOS regardless of FLTEnableSDFs value by @flutteractionsbot in [185680](https://github.com/flutter/flutter/pull/185680)
* [flutter-3.44-candidate.0] Update Flutter DEPS to Dart 9dc12969f5526d1bf1c2b48197d1608a68075866 by @flutteractionsbot in [185757](https://github.com/flutter/flutter/pull/185757)
* [flutter-3.44-candidate.0] Sync engine.version to 73dc1ccd62aec198da4aefde1dae20b1167b131d by @flutteractionsbot in [185771](https://github.com/flutter/flutter/pull/185771)
* Update dart version to 3.12.0-327.5.beta by @walley892 in [186276](https://github.com/flutter/flutter/pull/186276)
* [flutter-3.44-candidate.0] Sync engine.version to 4b520f6012e613925f891f7fdcbe731909fdfbd9 by @flutteractionsbot in [186279](https://github.com/flutter/flutter/pull/186279)
* [flutter-3.44-candidate.0] Update Flutter DEPS to Dart 98116461144f4429ab873f8497023a5ec3b08127 by @flutteractionsbot in [186420](https://github.com/flutter/flutter/pull/186420)
* [flutter-3.44-candidate.0] Sync engine.version to 4c525dac5ebe5971c5708ef73558ed8edcf4a362 by @flutteractionsbot in [186587](https://github.com/flutter/flutter/pull/186587)

## New contributors

Expand Down Expand Up @@ -1003,4 +1015,4 @@ This page has release notes for Flutter 3.44.
* @nikb7 made their first contribution in [183650](https://github.com/flutter/flutter/pull/183650)
* @TrangLeQuynh made their first contribution in [183488](https://github.com/flutter/flutter/pull/183488)

**Full Changelog**: https://github.com/flutter/flutter/compare/3.41.0...3.44.0-0.3.pre
**Full Changelog**: https://github.com/flutter/flutter/compare/3.41.0...3.44.0
2 changes: 2 additions & 0 deletions tool/dash_site/lib/dash_site.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'src/commands/check_links.dart';
import 'src/commands/clean.dart';
import 'src/commands/deploy.dart';
import 'src/commands/format_dart.dart';
import 'src/commands/generate_release_notes.dart';
import 'src/commands/refresh_excerpts.dart';
import 'src/commands/serve.dart';
import 'src/commands/stage_preview.dart';
Expand Down Expand Up @@ -44,6 +45,7 @@ final class DashSiteCommandRunner extends CommandRunner<int> {
addCommand(CleanSiteCommand());
addCommand(DeployCommand());
addCommand(FormatDartCommand());
addCommand(GenerateReleaseNotesCommand());
addCommand(RefreshExcerptsCommand());
addCommand(ServeSiteCommand());
addCommand(StagePreviewCommand());
Expand Down
214 changes: 214 additions & 0 deletions tool/dash_site/lib/src/commands/generate_release_notes.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// Copyright 2026 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:convert';
import 'dart:io';

import 'package:args/command_runner.dart';
import 'package:github/github.dart' as github;
import 'package:path/path.dart' as path;

import '../utils.dart';

const String _defaultConfigurationPath = '.github/release.yml';
const String _owner = 'flutter';
const String _repository = 'flutter';
const String _releaseNotesDirectory =
'sites/docs/src/content/release/release-notes';

final RegExp _stableVersionPattern = RegExp(r'^\d+\.\d+\.\d+$');

final class GenerateReleaseNotesCommand extends Command<int> {
static const String _configurationOption = 'configuration';
static const String _forceFlag = 'force';
static const String _outputOption = 'output';
static const String _previousTagOption = 'previous-tag';
static const String _releaseOption = 'release';
static const String _tagOption = 'tag';

GenerateReleaseNotesCommand() {
argParser.addOption(
_releaseOption,
abbr: 'r',
help: 'The stable Flutter release version used in the page metadata.',
valueHelp: 'version',
mandatory: true,
);
argParser.addOption(
_previousTagOption,
abbr: 'p',
help: 'The Flutter tag from which to start the generated changelog.',
valueHelp: 'tag',
mandatory: true,
);
argParser.addOption(
_tagOption,
abbr: 't',
help:
'The Flutter tag at which to end the generated changelog. '
'Defaults to the release version.',
valueHelp: 'tag',
);
argParser.addOption(
_configurationOption,
abbr: 'c',
defaultsTo: _defaultConfigurationPath,
help: 'The flutter/flutter release-note configuration file.',
valueHelp: 'path',
);
argParser.addOption(
_outputOption,
abbr: 'o',
help: 'The output Markdown path. Defaults to the docs release-note path.',
valueHelp: 'path',
);
argParser.addFlag(
_forceFlag,
abbr: 'f',
negatable: false,
help: 'Overwrite an existing output file.',
);
}

@override
String get description =>
'Generate a Flutter release-notes page from GitHub pull requests.';

@override
String get name => 'generate-release-notes';

@override
Future<int> run() async {
final argResults = this.argResults!;
final release = argResults.option(_releaseOption)!;
final previousTag = argResults.option(_previousTagOption)!;
final tag = argResults.option(_tagOption) ?? release;
final configuration = argResults.option(_configurationOption)!;
final output =
argResults.option(_outputOption) ??
path.join(
repositoryRoot,
_releaseNotesDirectory,
'release-notes-$release.md',
);
final force = argResults.flag(_forceFlag);

if (!_isStableFlutterVersion(release)) {
stderr.writeln(
"Error: '$release' isn't a stable Flutter version like 3.44.0.",
);
return 1;
}

final outputFile = File(output);
if (outputFile.existsSync() && !force) {
stderr.writeln(
'Error: ${outputFile.path} already exists. '
'Use --force to overwrite it.',
);
return 1;
}

final token = nonEmpty(Platform.environment['GITHUB_TOKEN']?.trim());
if (token == null) {
stderr.writeln(
'Error: Set GITHUB_TOKEN to a GitHub token that '
'can read flutter/flutter.',
);
return 1;
}

final gitHub = github.GitHub(auth: github.Authentication.withToken(token));

try {
print('Generating Flutter release notes from $previousTag to $tag...');
final notes = await gitHub.repositories.generateReleaseNotes(
github.CreateReleaseNotes(
_owner,
_repository,
tag,
previousTagName: previousTag,
configurationFilePath: configuration,
),
);
final page = _createReleaseNotesPage(release, notes.body);
Comment thread
parlough marked this conversation as resolved.

await outputFile.parent.create(recursive: true);
await outputFile.writeAsString(page);
print('Wrote Flutter $release release notes to ${outputFile.path}.');
return 0;
} on github.GitHubError catch (error) {
stderr.writeln('Error: GitHub failed to generate release notes: $error');
return 1;
} finally {
gitHub.dispose();
}
}
}

/// Whether [version] is a three-part stable Flutter version.
bool _isStableFlutterVersion(String version) =>
_stableVersionPattern.hasMatch(version);

/// Creates a docs.flutter.dev release-notes page for [release]
/// with the specified [generatedNotes].
String _createReleaseNotesPage(String release, String generatedNotes) {
final featureRelease = release.substring(0, release.lastIndexOf('.'));
final cleanedNotes = _cleanGeneratedReleaseNotes(generatedNotes);

return '''---
title: Flutter $release release notes
shortTitle: $release release notes
description: Release notes for Flutter $release.
skipTemplateRendering: true
---
This page has release notes for Flutter $featureRelease.
$cleanedNotes
''';
}

/// Cleans and normalizes the Markdown returned by GitHub's release-note API.
String _cleanGeneratedReleaseNotes(String generatedNotes) {
const generatedComment =
'<!-- Release notes generated using configuration in ';
final pullRequestUrl = RegExp(
r'https://github\.com/[^/\s]+/[^/\s]+/pull/(\d+)',
);
final cleanedLines = const LineSplitter()
.convert(generatedNotes)
.where((line) => !line.contains(generatedComment))
.map((line) {
// The '##' headings are fixed strings from GitHub's API,
// while 'Other Changes' is a category title in
// flutter/flutter's `release.yml` and can drift if that file changes.
final sentenceCaseHeading = switch (line) {
"## What's Changed" => "## What's changed",
'### Other Changes' => '### Other changes',
'## New Contributors' => '## New contributors',
_ => line,
};
return sentenceCaseHeading.replaceAllMapped(
pullRequestUrl,
(match) => '[${match[1]}](${match[0]})',
);
});

// Insert a blank line between each '##' heading and
// adjacent non-empty lines.
final normalizedLines = <String>[];
for (final line in cleanedLines) {
final previousLine = normalizedLines.lastOrNull;
if (line.isNotEmpty &&
previousLine != null &&
previousLine.isNotEmpty &&
(line.startsWith('##') || previousLine.startsWith('##'))) {
normalizedLines.add('');
}
normalizedLines.add(line);
}

return normalizedLines.join('\n').trim();
}
18 changes: 6 additions & 12 deletions tool/dash_site/lib/src/commands/stage_preview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ final class StagePreviewCommand extends Command<int> {
final project =
argResults.option(_projectOption) ??
selectedSite.defaultFirebaseProjectId;
final channel = _nonEmpty(argResults.option(_channelOption));
final channel = nonEmpty(argResults.option(_channelOption));
final expires = argResults.option(_expiresOption)!;
final prNumberArg = _nonEmpty(argResults.option(_prNumberOption));
final repoFullName = _nonEmpty(argResults.option(_repoOption));
final commitSha = _nonEmpty(argResults.option(_commitShaOption));
final headBranch = _nonEmpty(argResults.option(_headBranchOption));
final prNumberArg = nonEmpty(argResults.option(_prNumberOption));
final repoFullName = nonEmpty(argResults.option(_repoOption));
final commitSha = nonEmpty(argResults.option(_commitShaOption));
final headBranch = nonEmpty(argResults.option(_headBranchOption));

// Validate PR-context preconditions up front so a misconfigured
// trigger fails before the build and deploy runs unnecessarily.
Expand All @@ -121,7 +121,7 @@ final class StagePreviewCommand extends Command<int> {
return 1;
}
const githubPatTokenEnv = 'GH_PAT_TOKEN';
final githubToken = _nonEmpty(Platform.environment[githubPatTokenEnv]);
final githubToken = nonEmpty(Platform.environment[githubPatTokenEnv]);
if (githubToken == null) {
stderr.writeln(
'Error: $githubPatTokenEnv must be set to '
Expand Down Expand Up @@ -349,12 +349,6 @@ String _firebaseChannelForSite(
return channel.replaceAll(RegExp(r'-+$'), '');
}

/// Returns [value] if it is non-null and non-empty, otherwise `null`.
String? _nonEmpty(String? value) {
if (value == null || value.isEmpty) return null;
return value;
}

/// Everything required to post a preview comment on a GitHub pull request.
///
/// Built during option parsing so that a misconfigured Cloud Build trigger
Expand Down
6 changes: 6 additions & 0 deletions tool/dash_site/lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ int runPubGetIfNecessary(String directory) {
return 0;
}

/// Returns [value] if it is non-null and non-empty, otherwise `null`.
String? nonEmpty(String? value) {
if (value == null || value.isEmpty) return null;
return value;
}

extension ArgResultExtensions on ArgResults? {
/// Assuming its type is [T], get the value specified for
/// the argument named [key], or the [defaultValue] if not specified.
Expand Down
Loading