Skip to content

Commit 2d0e783

Browse files
authored
Use the changelog excerpt in the atom feeds. (#8944)
1 parent 2e2c92f commit 2d0e783

File tree

3 files changed

+68
-11
lines changed

3 files changed

+68
-11
lines changed

app/lib/frontend/handlers/atom_feed.dart

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import 'dart:async';
66
import 'dart:convert';
77

88
import 'package:clock/clock.dart';
9+
import 'package:collection/collection.dart';
910
import 'package:crypto/crypto.dart';
11+
import 'package:pub_dev/shared/changelog.dart';
1012
import 'package:shelf/shelf.dart' as shelf;
1113

1214
import '../../admin/actions/actions.dart';
@@ -53,7 +55,9 @@ Future<shelf.Response> packageAtomFeedhandler(
5355
Future<String> buildAllPackagesAtomFeedContent() async {
5456
final versions = await packageBackend.latestPackageVersions(limit: 100);
5557
versions.removeWhere((pv) => pv.isNotVisible || pv.isRetracted);
56-
final feed = _allPackagesFeed(versions);
58+
final contents = await Future.wait(
59+
versions.map((v) => _getChangelogReleaseContent(v.package, v.version!)));
60+
final feed = _allPackagesFeed(versions, contents);
5761
return feed.toXmlDocument();
5862
}
5963

@@ -70,10 +74,34 @@ Future<String> buildPackageAtomFeedContent(String package) async {
7074
)
7175
.toList();
7276
versions.removeWhere((pv) => pv.isNotVisible || pv.isRetracted);
73-
final feed = _packageFeed(package, versions);
77+
final contents = await Future.wait(
78+
versions.map((v) => _getChangelogReleaseContent(package, v.version!)));
79+
final feed = _packageFeed(package, versions, contents);
7480
return feed.toXmlDocument();
7581
}
7682

83+
Future<String> _getChangelogReleaseContent(
84+
String package, String version) async {
85+
final content = await cache
86+
.changelogReleaseContentAsMarkdown(package, version)
87+
.get(() async {
88+
final asset = await packageBackend.lookupPackageVersionAsset(
89+
package, version, AssetKind.changelog);
90+
final content = asset?.textContent;
91+
if (content == null) {
92+
return '';
93+
}
94+
final parsed = ChangelogParser().parseMarkdownText(content);
95+
final release =
96+
parsed.releases.firstWhereOrNull((r) => r.version == version);
97+
if (release == null) {
98+
return '';
99+
}
100+
return release.content.asMarkdownText;
101+
});
102+
return content ?? '';
103+
}
104+
77105
class FeedEntry {
78106
final String id;
79107
final String title;
@@ -181,7 +209,10 @@ class Feed {
181209
}
182210
}
183211

184-
Feed _allPackagesFeed(List<PackageVersion> versions) {
212+
Feed _allPackagesFeed(
213+
List<PackageVersion> versions,
214+
List<String> releaseContents,
215+
) {
185216
final entries = <FeedEntry>[];
186217
for (var i = 0; i < versions.length; i++) {
187218
final version = versions[i];
@@ -195,7 +226,14 @@ Feed _allPackagesFeed(List<PackageVersion> versions) {
195226
sha512.convert(utf8.encode('${version.package}/${version.version}'));
196227
final id = createUuid(hash.bytes.sublist(0, 16));
197228
final title = 'v${version.version} of ${version.package}';
198-
final content = version.ellipsizedDescription ?? '[no description]';
229+
final fullReleaseContent = releaseContents[i];
230+
final releaseContent = fullReleaseContent.length > 512
231+
? '${fullReleaseContent.substring(0, 500)}[...]'
232+
: fullReleaseContent;
233+
final content = [
234+
version.ellipsizedDescription ?? '[no description]',
235+
if (releaseContent.isNotEmpty) 'Changelog excerpt:\n$releaseContent',
236+
].join('\n\n');
199237
entries.add(FeedEntry(
200238
id: id,
201239
title: title,
@@ -216,7 +254,11 @@ Feed _allPackagesFeed(List<PackageVersion> versions) {
216254
);
217255
}
218256

219-
Feed _packageFeed(String package, List<PackageVersion> versions) {
257+
Feed _packageFeed(
258+
String package,
259+
List<PackageVersion> versions,
260+
List<String> releaseContents,
261+
) {
220262
return Feed(
221263
title: 'Recently published versions of package $package on pub.dev',
222264
alternateUrl: activeConfiguration.primarySiteUri
@@ -227,7 +269,7 @@ Feed _packageFeed(String package, List<PackageVersion> versions) {
227269
.resolve(urls.pkgFeedUrl(package))
228270
.toString(),
229271
author: versions.firstOrNull?.publisherId,
230-
entries: versions.map((v) {
272+
entries: versions.mapIndexed((i, v) {
231273
final hash =
232274
sha512.convert(utf8.encode('package-feed/$package/${v.version}'));
233275
final id = createUuid(hash.bytes.sublist(0, 16));
@@ -238,13 +280,19 @@ Feed _packageFeed(String package, List<PackageVersion> versions) {
238280
version: v.version,
239281
))
240282
.toString();
283+
final fullReleaseContent = releaseContents[i];
284+
final releaseContent = fullReleaseContent.length > 1024
285+
? '${fullReleaseContent.substring(0, 1000)}[...]'
286+
: fullReleaseContent;
241287
return FeedEntry(
242288
id: id,
243289
title: 'v${v.version} of $package',
244290
alternateUrl: alternateUrl,
245291
alternateTitle: v.version,
246-
content:
247-
'${v.version} was published on ${shortDateFormat.format(v.created!)}.',
292+
content: [
293+
'${v.version} was published on ${shortDateFormat.format(v.created!)}.',
294+
if (releaseContent.isNotEmpty) 'Changelog excerpt:\n$releaseContent',
295+
].join('\n\n'),
248296
updated: v.created!,
249297
);
250298
}).toList(),

app/lib/shared/redis_cache.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ class CachePatterns {
130130
decode: (d) => d as bool,
131131
))[package];
132132

133+
Entry<String> changelogReleaseContentAsMarkdown(
134+
String package, String version) =>
135+
_cache
136+
.withPrefix('changelog-release-content-md/')
137+
.withTTL(Duration(hours: 1))
138+
.withCodec(utf8)['$package-$version'];
139+
133140
Entry<List<int>> packageData(String package) => _cache
134141
.withPrefix('api-package-data-by-uri/')
135142
.withTTL(Duration(minutes: 10))['$package'];

app/test/frontend/handlers/atom_feed_test.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,22 @@ void main() {
3636
' <id>urn:uuid:a6a43bff-e1ef-4633-b5ee-e0516b655be9</id>\n'
3737
' <title>v1.2.0 of oxygen</title>\n'
3838
' <updated>(.*)</updated>\n'
39-
' <content>oxygen is awesome</content>\n'
39+
// Note: pretty format + indenting converts the newlines into spaces.
40+
' <content>oxygen is awesome Changelog excerpt: - updated</content>\n'
4041
' <link href="${activeConfiguration.primarySiteUri}/packages/oxygen" rel="alternate" title="oxygen"/>\n'
4142
'</entry>');
4243
expect(
4344
oxygenExpr.hasMatch(entries[1].toXmlString(pretty: true, indent: ' ')),
4445
isTrue,
45-
reason: entries[1].toXmlString(),
46+
reason: entries[1].toXmlString(pretty: true, indent: ' '),
4647
);
4748

4849
final neonExpr = RegExp('<entry>\n'
4950
' <id>urn:uuid:5f920595-c067-404a-bb19-2b0918372eb6</id>\n'
5051
' <title>v1.0.0 of neon</title>\n'
5152
' <updated>(.*)</updated>\n'
52-
' <content>neon is awesome</content>\n'
53+
// Note: pretty format + indenting converts the newlines into spaces.
54+
' <content>neon is awesome Changelog excerpt: - updated</content>\n'
5355
' <link href="${activeConfiguration.primarySiteUri}/packages/neon" rel="alternate" title="neon"/>\n'
5456
'</entry>');
5557
expect(

0 commit comments

Comments
 (0)