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
3 changes: 2 additions & 1 deletion packages/devtools_app/lib/src/framework/about_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class DevToolsAboutDialog extends StatelessWidget {
'release notes',
style: theme.linkTextStyle,
),
onTap: () => releaseNotesController.toggleVisibility(true),
onTap: () =>
unawaited(releaseNotesController.openLatestReleaseNotes()),
),
],
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import '../../shared/side_panel.dart';

final _log = Logger('release_notes');

const debugTestReleaseNotes = false;
// This is not const because it is manipulated for testing as well as for
// local development.
bool debugTestReleaseNotes = false;
const debugUseStagedFlutterWebsite = false;
const releaseNotesKey = Key('release_notes');

class ReleaseNotesViewer extends SidePanelViewer {
Expand All @@ -37,17 +40,17 @@ class ReleaseNotesController extends SidePanelController {

static const _unsupportedPathSyntax = '{{site.url}}';

String get _flutterDocsSite => debugTestReleaseNotes
String get _flutterDocsSite => debugUseStagedFlutterWebsite
? 'https://flutter-website-dt-staging.web.app'
: 'https://docs.flutter.dev';

void _init() {
if (debugTestReleaseNotes || server.isDevToolsServerAvailable) {
_maybeFetchReleaseNotes();
_maybeShowReleaseNotes();
}
}

void _maybeFetchReleaseNotes() async {
void _maybeShowReleaseNotes() async {
SemanticVersion previousVersion = SemanticVersion();
if (server.isDevToolsServerAvailable) {
final lastReleaseNotesShownVersion =
Expand All @@ -56,63 +59,102 @@ class ReleaseNotesController extends SidePanelController {
previousVersion = SemanticVersion.parse(lastReleaseNotesShownVersion);
}
}
await _fetchAndShowReleaseNotes(
versionFloor: debugTestReleaseNotes ? null : previousVersion,
);
}

/// Fetches and shows the most recent release notes for the current DevTools
/// version, decreasing the patch version by 1 each time until we find release
/// notes or until we hit [versionFloor].
Future<void> _fetchAndShowReleaseNotes({
SemanticVersion? versionFloor,
}) async {
versionFloor ??= SemanticVersion();

// Parse the current version instead of using [devtools.version] directly to
// strip off any build metadata (any characters following a '+' character).
// Release notes will be hosted on the Flutter website with a version number
// that does not contain any build metadata.
final parsedCurrentVersion = SemanticVersion.parse(devtools.version);
final parsedCurrentVersionStr = parsedCurrentVersion.toString();
if (parsedCurrentVersion > previousVersion) {
try {
await _fetchReleaseNotes(parsedCurrentVersion);
} catch (e) {
// Fail gracefully if we cannot find release notes for the current
// version of DevTools.
markdownText = null;
toggleVisibility(false);
_log.warning(
'Warning: could not find release notes for DevTools version '
'$parsedCurrentVersionStr. $e',
);
final parsedVersion = SemanticVersion.parse(devtools.version);
var notesVersion = latestVersionToCheckForReleaseNotes(parsedVersion);
try {
// Try all patch versions for this major.minor combination until we find
// release notes (e.g. 2.11.4 -> 2.11.3 -> 2.11.2 -> ...).
final attemptedVersions = <String>[];
var attempts = notesVersion.patch;
while (attempts >= 0 && notesVersion > versionFloor) {

@CoderDake CoderDake May 11, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are network issues requesting a release version we are looking for, it appears we might skip that and then try requesting the next version down.

Instead of searching for the version by looking for the first available high version, would it be possible for us to update our release process/helpers to change a constant in the project? It could signify the last release we have done, then we could just plug that constant in here, then try to fetch just that version.

It would probably have a healthier lifecycle too as we could choose how we want to fail if that specific request for the version doesn't end up succeeding.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What release helper are you referring to? I worry that as we approach automated SDK releases, our build_release.sh script will be used to build dev releases and push them into the SDK, in addition to DevTools stable releases. But only stable releases will have release notes, so we wouldn't want build_release.sh to be updating some constant. It also might be difficult to enforce that a const in DevTools is updated when a change is made to the flutter website.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are network issues requesting a release version we are looking for, it appears we might skip that and then try requesting the next version down

If the network requests all fail, we will eventually catch and log a warning with all the release notes versions we tried to fetch. This isn't new behavior from the previous state, though now we will try a max of 10 times where before we would only try 'patch' number of times where 'patch' was the current devtools version's patch value.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm fair enough that we need to be in sync with the flutter website so that might be tricky. Although, In the current implementation if the flutter website wasn't updated yet, that we would still be showing the wrong release notes though :/ 🤔 We would technically show old release notes until the new ones were posted up.

In the case that the flutter website notes are up to date, I think the case I'd still be worried about is if only the request to the proper version fails and then the 2nd request passes. Then I think we would skip a version and show old release notes.

I think the current approach could be fine for now but I wonder if the better behaviour would be to show no release notes if the exact version we want to show isn't available.

I think if we wanted to let DevTools know which release notes to show, we could put a const here: https://github.com/flutter/devtools/blob/master/packages/devtools_app/lib/devtools.dart

We could update it any time we de a minor update in update_version.dart:
https://github.com/flutter/devtools/blob/master/tool/update_version.dart#L48

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although, In the current implementation if the flutter website wasn't updated yet, that we would still be showing the wrong release notes though

Not necessarily. We only decrease patch versions looking for release notes. So for example, if I release DevTools 2.25.1 and I haven't published flutter website release notes for 2.25.1, then this code will only look for 2.25.1 and 2.25.0. We will not decrease the minor version and keep looking. We don't push up release notes for new patch versions - only for minor version bumps with the monthly release.

I think the case I'd still be worried about is if only the request to the proper version fails and then the 2nd request passes. Then I think we would skip a version and show old release notes.

The same scenario applies here. Because we only decrease patch versions looking for notes, and we only write release notes for minor versions, there is not a risk in the current state of showing release notes for the prior release.

What I do like about this approach is that release notes will just load if they are available, and we don't have another bit we have to set manually to ensure our system works. However, I see your point about adding a const so that we don't have to send more than one http request. Updating this const for every minor update does fall apart a little for bumping to a dev version after release though.

For example, when we released 2.23.1, we immediately follow that release with a version bump to 2.24.0-dev.1. So while 2.24.0-dev.1 is a minor + dev bump from 2.23.1, we still want to show the release notes for 2.23.1 if a user is on 2.24.0-dev.1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM about my concern with network stuff.

For the version bumping though, I might not have said it the right way. I guess it would be each time we make a release version we would be changing that const. Since those release versions are the ones that we are pushing up to the website.

For now though it seems that the searching method should be fine though.

final versionString = notesVersion.toString();
try {
String releaseNotesMarkdown = await http.read(
Uri.parse(_releaseNotesUrl(versionString)),
);
// This is a workaround so that the images in release notes will appear.
// The {{site.url}} syntax is best practices for the flutter website
// repo, where these release notes are hosted, so we are performing this
// workaround on our end to ensure the images render properly.
releaseNotesMarkdown = releaseNotesMarkdown.replaceAll(
_unsupportedPathSyntax,
_flutterDocsSite,
);

markdown.value = releaseNotesMarkdown;
toggleVisibility(true);
if (server.isDevToolsServerAvailable) {
unawaited(
server.setLastShownReleaseNotesVersion(versionString),
);
}
return;
} catch (e) {
attempts--;
attemptedVersions.add(versionString);
if (attempts < 0) {
// ignore: avoid-throw-in-catch-block, false positive
throw Exception(
'Could not find release notes for DevTools versions '
'${attemptedVersions.join(', ')}.'
'\n$e',
);
}
notesVersion = notesVersion.downgrade(downgradePatch: true);
}
}
} catch (e) {
// Fail gracefully if we cannot find release notes for the current
// version of DevTools.
markdown.value = null;
toggleVisibility(false);
_log.warning('Warning: $e');
}
}

Future<void> _fetchReleaseNotes(SemanticVersion version) async {
final currentVersionString = version.toString();

// Try all patch versions for this major.minor combination until we find
// release notes (e.g. 2.11.4 -> 2.11.3 -> 2.11.2 -> ...).
var attempts = version.patch;
while (attempts >= 0) {
final versionString = version.toString();
try {
String releaseNotesMarkdown = await http.read(
Uri.parse(_releaseNotesUrl(versionString)),
);
// This is a workaround so that the images in release notes will appear.
// The {{site.url}} syntax is best practices for the flutter website
// repo, where these release notes are hosted, so we are performing this
// workaround on our end to ensure the images render properly.
releaseNotesMarkdown = releaseNotesMarkdown.replaceAll(
_unsupportedPathSyntax,
_flutterDocsSite,
);
@visibleForTesting
SemanticVersion latestVersionToCheckForReleaseNotes(
SemanticVersion currentVersion,
) {
// If the current version is a pre-release, downgrade the minor to find the
// previous DevTools release, and start looking for release notes from this
// value. Release notes will never be published for pre-release versions.
if (currentVersion.isPreRelease) {
// It is very unlikely the patch value of the DevTools version will ever
// be above this number. This is a safe number to start looking for
// release notes at.
const safeStartPatch = 10;
currentVersion = SemanticVersion(
major: currentVersion.major,
minor: currentVersion.minor - 1,
patch: safeStartPatch,
);
}
return currentVersion;
}

markdownText = releaseNotesMarkdown;
toggleVisibility(true);
unawaited(
server.setLastShownReleaseNotesVersion(currentVersionString),
);
return;
} catch (_) {
attempts--;
if (attempts < 0) {
rethrow;
}
version = version.downgrade(downgradePatch: true);
}
Future<void> openLatestReleaseNotes() async {
if (markdown.value == null) {
await _fetchAndShowReleaseNotes();
}
toggleVisibility(true);
}

String _releaseNotesUrl(String currentVersion) {
Expand Down
8 changes: 1 addition & 7 deletions packages/devtools_app/lib/src/shared/side_panel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,7 @@ class SidePanel extends AnimatedWidget {
}

class SidePanelController {
ValueListenable<String?> get markdown => _markdown;

final _markdown = ValueNotifier<String?>(null);
final markdown = ValueNotifier<String?>(null);

ValueListenable<bool> get isVisible => _isVisible;

Expand All @@ -205,8 +203,4 @@ class SidePanelController {
void toggleVisibility(bool visible) {
_isVisible.value = visible;
}

set markdownText(String? markdownText) {
_markdown.value = markdownText;
}
}
7 changes: 3 additions & 4 deletions packages/devtools_app/release_notes/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
## Generating Release notes
- Release notes for DevTools are hosted on the flutter website (see [archive](https://docs.flutter.dev/development/tools/devtools/release-notes)).
- Release notes for DevTools are hosted on the flutter website (see [archive](https://docs.flutter.dev/tools/devtools/release-notes)).
- To add release notes for the latest release, create a PR with the appropriate changes for your release.
- The [NEXT_RELEASE_NOTES.md](NEXT_RELEASE_NOTES.md) file contains the running release notes for the current version.
- see example [PR](https://github.com/flutter/website/pull/6791) for an idea of how to add those to the Flutter website.
- NOTE: when adding images, be cognizant that the images will be rendered in a relatively small window in DevTools, and should be sized accordingly.
- Test these changes locally before creating the PR.
- See [README.md](https://github.com/flutter/website/blob/main/README.md)
for getting setup to run the Flutter website locally.
- Release notes can be found at [http://localhost:4002/development/tools/devtools/release-notes/](http://localhost:4002/development/tools/devtools/release-notes/)
- Release notes can be found at [http://localhost:4002/development/tools/devtools/release-notes/](http://localhost:4002/tools/devtools/release-notes/)

- Once you are satisfied with the release notes
- stage the Flutter website on Firebase
Expand Down Expand Up @@ -47,7 +47,7 @@ for getting setup to run the Flutter website locally.
```

### Testing the release notes in DevTools
- In `release_notes.dart` flip the `debugTestReleaseNotes` flag to true.
- In `release_notes.dart` flip the `debugTestReleaseNotes` and `debugUseStagedFlutterWebsite` flags to true.

- from the main `devtools/` directory, run the following:
```shell
Expand All @@ -64,4 +64,3 @@ Serving DevTools at http://127.0.0.1:57336.

- Visit the DevTools link
- verify the release notes viewer displays the new release notes as expected.

29 changes: 29 additions & 0 deletions packages/devtools_app/test/shared/release_notes_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2023 The Chromium 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 'package:devtools_app/devtools_app.dart';
import 'package:devtools_shared/devtools_shared.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('$ReleaseNotesController', () {
late ReleaseNotesController controller;
setUp(() {
debugTestReleaseNotes = true;
controller = ReleaseNotesController();
});

test('latestVersionToCheckForReleaseNotes', () {
var version = controller.latestVersionToCheckForReleaseNotes(
SemanticVersion.parse('2.24.5-dev.1'),
);
expect(version.toString(), '2.23.10');

version = controller.latestVersionToCheckForReleaseNotes(
SemanticVersion.parse('2.24.1'),
);
expect(version.toString(), '2.24.1');
});
});
}