@@ -882,8 +882,9 @@ class PackageBackend {
882882 );
883883 }
884884
885- /// Finishes the upload of a package.
886- Future <PackageVersion > publishUploadedBlob (String guid) async {
885+ /// Finishes the upload of a package and returns the list of messages
886+ /// related to the publishing.
887+ Future <List <String >> publishUploadedBlob (String guid) async {
887888 final restriction = await getUploadRestrictionStatus ();
888889 if (restriction == UploadRestrictionStatus .noUploads) {
889890 throw PackageRejectedException .uploadRestricted ();
@@ -984,7 +985,7 @@ class PackageBackend {
984985 sw.reset ();
985986 final entities = await _createUploadEntities (db, agent, archive,
986987 sha256Hash: sha256Hash);
987- final version = await _performTarballUpload (
988+ final ( version, uploadMessages) = await _performTarballUpload (
988989 entities: entities,
989990 agent: agent,
990991 archive: archive,
@@ -998,7 +999,13 @@ class PackageBackend {
998999 sw.reset ();
9991000 await _incomingBucket.deleteWithRetry (tmpObjectName (guid));
10001001 _logger.info ('Temporary object removed in ${sw .elapsed }.' );
1001- return version;
1002+ return [
1003+ 'Successfully uploaded '
1004+ '${urls .pkgPageUrl (version .package , includeHost : true )} '
1005+ 'version ${version .version }, '
1006+ 'it may take up-to 10 minutes before the new version is available.' ,
1007+ ...uploadMessages,
1008+ ];
10021009 });
10031010 }
10041011
@@ -1057,14 +1064,15 @@ class PackageBackend {
10571064 }
10581065 }
10591066
1060- Future <PackageVersion > _performTarballUpload ({
1067+ Future <( PackageVersion , List < String >) > _performTarballUpload ({
10611068 required _UploadEntities entities,
10621069 required AuthenticatedAgent agent,
10631070 required PackageSummary archive,
10641071 required String guid,
10651072 required bool hasCanonicalArchiveObject,
10661073 }) async {
10671074 final sw = Stopwatch ()..start ();
1075+ final uploadMessages = < String > [];
10681076 final newVersion = entities.packageVersion;
10691077 final [currentDartSdk, currentFlutterSdk] = await Future .wait ([
10701078 getCachedDartSdkVersion (lastKnownStable: toolStableDartSdkVersion),
@@ -1100,14 +1108,6 @@ class PackageBackend {
11001108 package: newVersion.package,
11011109 isNew: isNew,
11021110 );
1103- final email = createPackageUploadedEmail (
1104- packageName: newVersion.package,
1105- packageVersion: newVersion.version! ,
1106- displayId: agent.displayId,
1107- authorizedUploaders:
1108- uploaderEmails.map ((email) => EmailAddress (email)).toList (),
1109- );
1110- final outgoingEmail = emailBackend.prepareEntity (email);
11111111 Package ? package;
11121112 final existingVersions = await db
11131113 .query <PackageVersion >(ancestorKey: newVersion.packageKey! )
@@ -1116,7 +1116,7 @@ class PackageBackend {
11161116
11171117 // Add the new package to the repository by storing the tarball and
11181118 // inserting metadata to datastore (which happens atomically).
1119- final pv = await withRetryTransaction (db, (tx) async {
1119+ final (pv, outgoingEmail) = await withRetryTransaction (db, (tx) async {
11201120 _logger.info ('Starting datastore transaction.' );
11211121
11221122 final tuple = (await tx.lookup ([
@@ -1156,10 +1156,21 @@ class PackageBackend {
11561156
11571157 final maxVersionCount = maxVersionsPerPackageOverrides[package! .name] ??
11581158 maxVersionsPerPackage;
1159- if (package! .versionCount >= maxVersionCount) {
1159+ final remainingVersionCount = maxVersionCount - package! .versionCount;
1160+ if (remainingVersionCount <= 0 ) {
11601161 throw PackageRejectedException .maxVersionCountReached (
11611162 newVersion.package, maxVersionCount);
11621163 }
1164+ if (remainingVersionCount <= 100 ) {
1165+ // We need to decrease the remaining version count as the newly uploaded
1166+ // version is not yet in it.
1167+ final limitAfterUpload = remainingVersionCount - 1 ;
1168+ final s = limitAfterUpload == 1 ? '' : 's' ;
1169+ uploadMessages.add (
1170+ 'The package "${package !.name !}" has $limitAfterUpload version$s left '
1171+ 'before reaching the limit of $maxVersionCount . '
1172+ 'Please contact [email protected] ' );
1173+ }
11631174
11641175 if (package! .deletedVersions != null &&
11651176 package! .deletedVersions! .contains (newVersion.version! )) {
@@ -1196,6 +1207,16 @@ class PackageBackend {
11961207 await tarballStorage.copyArchiveFromCanonicalToPublicBucket (
11971208 newVersion.package, newVersion.version! );
11981209
1210+ final email = createPackageUploadedEmail (
1211+ packageName: newVersion.package,
1212+ packageVersion: newVersion.version! ,
1213+ displayId: agent.displayId,
1214+ authorizedUploaders:
1215+ uploaderEmails.map ((email) => EmailAddress (email)).toList (),
1216+ uploadMessages: uploadMessages,
1217+ );
1218+ final outgoingEmail = emailBackend.prepareEntity (email);
1219+
11991220 final inserts = < Model > [
12001221 package! ,
12011222 newVersion,
@@ -1221,7 +1242,7 @@ class PackageBackend {
12211242
12221243 _logger.info ('Trying to commit datastore changes.' );
12231244 tx.queueMutations (inserts: inserts);
1224- return newVersion;
1245+ return ( newVersion, outgoingEmail) ;
12251246 });
12261247 _logger.info ('Upload successful. [package-uploaded]' );
12271248 _logger.info ('Upload transaction completed in ${sw .elapsed }.' );
@@ -1237,7 +1258,7 @@ class PackageBackend {
12371258 .addAsyncFn (() => _postUploadTasks (package, newVersion, outgoingEmail));
12381259
12391260 _logger.info ('Post-upload tasks completed in ${sw .elapsed }.' );
1240- return pv ;
1261+ return (pv, uploadMessages) ;
12411262 }
12421263
12431264 /// The post-upload tasks are not critical and could fail without any impact on
0 commit comments