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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ Important changes to data models, configuration, and migrations between each
AppEngine version, listed here to ease deployment and troubleshooting.

## Next Release (replace with git tag when deployed)
* Note: after the release we should update the load balancer rules to also include the `/api/packages/<package>/feed.atom` URLs.

## `20250403t085600-all`
* Bump runtimeVersion to `2025.04.01`.
Expand Down
3 changes: 3 additions & 0 deletions app/lib/admin/actions/moderate_package.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ Note: the action may take a longer time to complete as the public archive bucket
return pkg;
});

// make sure visibility cache is updated immediately
await purgePackageCache(package);

// sync exported API(s)
await apiExporter?.synchronizePackage(package, forceDelete: true);

Expand Down
3 changes: 3 additions & 0 deletions app/lib/admin/actions/moderate_package_versions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ Set the moderated flag on a package version (updating the flag and the timestamp
return v;
});

// make sure visibility cache is updated immediately
await purgePackageCache(package);

// sync exported API(s)
await apiExporter?.synchronizePackage(package, forceDelete: true);

Expand Down
2 changes: 1 addition & 1 deletion app/lib/frontend/handlers/atom_feed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Future<shelf.Response> allPackagesAtomFeedhandler(shelf.Request request) async {
);
}

/// Handles requests for `/packages/<package>/feed.atom`
/// Handles requests for `/api/packages/<package>/feed.atom`
Future<shelf.Response> packageAtomFeedhandler(
shelf.Request request,
String package,
Expand Down
7 changes: 7 additions & 0 deletions app/lib/frontend/handlers/pubapi.client.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions app/lib/frontend/handlers/pubapi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:_pub_shared/data/package_api.dart';
import 'package:_pub_shared/data/publisher_api.dart';
import 'package:_pub_shared/data/task_api.dart';
import 'package:api_builder/api_builder.dart';
import 'package:pub_dev/frontend/handlers/atom_feed.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';

Expand Down Expand Up @@ -215,6 +216,11 @@ class PubApi {
) async =>
await packageBackend.inviteUploader(package, invite);

/// Renders the Atom XML feed for the package.
@EndPoint.get('/api/packages/<package>/feed.atom')
Future<Response> packageAtomFeed(Request request, String package) =>
packageAtomFeedhandler(request, package);

// ****
// **** Publisher API
// ****
Expand Down
20 changes: 20 additions & 0 deletions app/lib/frontend/handlers/pubapi.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions app/lib/frontend/handlers/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,6 @@ class PubSiteService {
Future<Response> packageVersions(Request request, String package) =>
packageVersionsListHandler(request, package);

/// Renders the Atom XML feed for the package.
@Route.get('/packages/<package>/feed.atom')
Future<Response> packageAtomFeed(Request request, String package) =>
packageAtomFeedhandler(request, package);

@Route.get('/packages/<package>')
Future<Response> package(Request request, String package) =>
packageVersionHandlerHtml(request, package);
Expand Down
5 changes: 0 additions & 5 deletions app/lib/frontend/handlers/routes.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions app/lib/package/api_export/api_exporter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ final class ApiExporter {
versionListing,
forceWrite: forceWrite,
);
await _api.package(package).feedAtomFile.write(
await buildPackageAtomFeedContent(package),
forceWrite: forceWrite,
);
}

/// Scan for updates from packages until [abort] is resolved, or [claim]
Expand Down
8 changes: 8 additions & 0 deletions app/lib/package/api_export/exported_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,13 @@ final class ExportedPackage {
ExportedJsonFile<ListAdvisoriesResponse> get advisories =>
_suffix<ListAdvisoriesResponse>('/advisories');

/// Interface for writing `/api/packages/<package>/feed.atom`
ExportedAtomFeedFile get feedAtomFile => ExportedAtomFeedFile._(
_owner,
'/api/packages/$_package/feed.atom',
Duration(hours: 12),
);

/// Interace for writing `/api/archives/<package>-<version>.tar.gz`.
ExportedBlob tarball(String version) => ExportedBlob._(
_owner,
Expand Down Expand Up @@ -399,6 +406,7 @@ final class ExportedPackage {
await Future.wait([
_owner._pool.withResource(() async => await versions.delete()),
_owner._pool.withResource(() async => await advisories.delete()),
_owner._pool.withResource(() async => await feedAtomFile.delete()),
..._owner._prefixes.map((prefix) async {
await _owner._listBucket(
prefix: prefix + '/api/archives/$_package-',
Expand Down
2 changes: 1 addition & 1 deletion app/lib/shared/urls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ String pkgArchiveDownloadUrl(String package, String version, {Uri? baseUri}) {
}

String pkgFeedUrl(String package) {
return '/packages/$package/feed.atom';
return '/api/packages/$package/feed.atom';
}

String pkgDocUrl(
Expand Down
2 changes: 2 additions & 0 deletions app/test/admin/exported_api_sync_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,13 @@ void main() {
'$runtimeVersion/api/archives/oxygen-2.0.0-dev.tar.gz',
'$runtimeVersion/api/packages/oxygen',
'$runtimeVersion/api/packages/oxygen/advisories',
'$runtimeVersion/api/packages/oxygen/feed.atom',
'latest/api/archives/oxygen-1.0.0.tar.gz',
'latest/api/archives/oxygen-1.2.0.tar.gz',
'latest/api/archives/oxygen-2.0.0-dev.tar.gz',
'latest/api/packages/oxygen',
'latest/api/packages/oxygen/advisories',
'latest/api/packages/oxygen/feed.atom',
});

final oxygenDataJson = data['latest/api/packages/oxygen'];
Expand Down
8 changes: 4 additions & 4 deletions app/test/frontend/handlers/atom_feed_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ void main() {
});
});

testWithProfile('/packages/<package>/feed.atom', fn: () async {
testWithProfile('/api/packages/<package>/feed.atom', fn: () async {
final content = await expectAtomXmlResponse(
await issueGet('/packages/oxygen/feed.atom'));
await issueGet('/api/packages/oxygen/feed.atom'));
// check if content is valid XML
final root = XmlDocument.parse(content);
final feed = root.rootElement;
Expand Down Expand Up @@ -110,11 +110,11 @@ void main() {
entries.forEach((e) => e.parent!.children.remove(e));

final restExp = RegExp('<feed xmlns="http://www.w3.org/2005/Atom">\n'
' <id>${activeConfiguration.primarySiteUri}/packages/oxygen/feed.atom</id>\n'
' <id>${activeConfiguration.primarySiteUri}/api/packages/oxygen/feed.atom</id>\n'
' <title>Recently published versions of package oxygen on pub.dev</title>\n'
' <updated>(.*)</updated>\n'
' <link href="${activeConfiguration.primarySiteUri}/packages/oxygen" rel="alternate"/>\n'
' <link href="${activeConfiguration.primarySiteUri}/packages/oxygen/feed.atom" rel="self"/>\n'
' <link href="${activeConfiguration.primarySiteUri}/api/packages/oxygen/feed.atom" rel="self"/>\n'
' <generator version="0.1.0">Pub Feed Generator</generator>\n'
' <subtitle>oxygen is awesome</subtitle>\n'
'(\\s*)'
Expand Down
24 changes: 24 additions & 0 deletions app/test/package/api_export/api_exporter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ Future<void> _testExportedApiSynchronization(
await bucket.readBytes('$runtimeVersion/api/archives/foo-1.0.0.tar.gz'),
isNotNull,
);
expect(
await bucket.readBytes('$runtimeVersion/api/packages/foo/feed.atom'),
isNotNull,
);
expect(
await bucket.readGzippedJson('$runtimeVersion/api/packages/foo'),
{
Expand All @@ -152,6 +156,10 @@ Future<void> _testExportedApiSynchronization(
await bucket.readString('$runtimeVersion/feed.atom'),
contains('v1.0.0 of foo'),
);
expect(
await bucket.readString('$runtimeVersion/api/packages/foo/feed.atom'),
contains('v1.0.0 of foo'),
);
}

_log.info('## New package');
Expand Down Expand Up @@ -185,6 +193,10 @@ Future<void> _testExportedApiSynchronization(
await bucket.readString('latest/feed.atom'),
contains('v1.0.0 of foo'),
);
expect(
await bucket.readString('latest/api/packages/foo/feed.atom'),
contains('v1.0.0 of foo'),
);
// Note. that name completion data won't be updated until search caches
// are purged, so we won't test that it is updated.

Expand All @@ -205,6 +217,10 @@ Future<void> _testExportedApiSynchronization(
await bucket.readString('latest/feed.atom'),
contains('v2.0.0 of bar'),
);
expect(
await bucket.readString('latest/api/packages/bar/feed.atom'),
contains('v2.0.0 of bar'),
);
}

_log.info('## New package version');
Expand Down Expand Up @@ -247,6 +263,10 @@ Future<void> _testExportedApiSynchronization(
await bucket.readString('$runtimeVersion/feed.atom'),
contains('v3.0.0 of bar'),
);
expect(
await bucket.readString('latest/api/packages/bar/feed.atom'),
contains('v3.0.0 of bar'),
);
}

_log.info('## Discontinued flipped on');
Expand Down Expand Up @@ -424,6 +444,10 @@ Future<void> _testExportedApiSynchronization(
await bucket.readGzippedJson('latest/api/packages/bar'),
isNull,
);
expect(
await bucket.readGzippedJson('latest/api/packages/feed.atom'),
isNull,
);
expect(
await bucket.readBytes('latest/api/archives/bar-2.0.0.tar.gz'),
isNull,
Expand Down
7 changes: 7 additions & 0 deletions pkg/_pub_shared/lib/src/pubapi.client.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/pub_integration/lib/script/public_pages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ class PublicPagesScript {
'Pub Feed Generator',
);
_contains(
await _pubClient.getContent('/packages/retry/feed.atom'),
'/packages/retry/feed.atom',
await _pubClient.getContent('/api/packages/retry/feed.atom'),
'/api/packages/retry/feed.atom',
);
}

Expand Down