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: new `Package.publishingConfig` field in Datastore, new `PUT /api/packages/<package>/publishing` endpoint.
* Note: Reverted public `cache-control` headers except for `/documentation/` pages.

## `20251023t081900-all`
Expand Down
20 changes: 17 additions & 3 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.

11 changes: 9 additions & 2 deletions app/lib/frontend/handlers/pubapi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -402,10 +402,17 @@ class PubApi {
) => putPackageOptionsHandler(request, package, body);

@EndPoint.put('/api/packages/<package>/automated-publishing')
Future<AutomatedPublishingConfig> setAutomatedPublishing(
Future<PkgPublishingConfig> setAutomatedPublishing(
Request request,
String package,
AutomatedPublishingConfig body,
PkgPublishingConfig body,
) => packageBackend.setAutomatedPublishing(package, body);

@EndPoint.put('/api/packages/<package>/publishing')
Future<PkgPublishingConfig> setPackagePublishing(
Request request,
String package,
PkgPublishingConfig body,
) => packageBackend.setAutomatedPublishing(package, body);

@EndPoint.get('/api/packages/<package>/versions/<version>/options')
Expand Down
24 changes: 22 additions & 2 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.

6 changes: 3 additions & 3 deletions app/lib/frontend/templates/views/pkg/admin_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,8 @@ d.Node packageAdminPageNode({
}

d.Node _automatedPublishing(Package package) {
final github = package.automatedPublishing?.githubConfig;
final gcp = package.automatedPublishing?.gcpConfig;
final github = package.publishingConfig?.githubConfig;
final gcp = package.publishingConfig?.gcpConfig;
final isGitHubEnabled = github?.isEnabled ?? false;
return d.fragment([
d.a(name: 'automated-publishing'),
Expand Down Expand Up @@ -469,7 +469,7 @@ d.Node _automatedPublishing(Package package) {
}

d.Node _manualPublishing(Package package) {
final manual = package.automatedPublishing?.manualConfig;
final manual = package.publishingConfig?.manualConfig;
return d.fragment([
d.a(name: 'manual-publishing'),
d.h3(text: 'Manual publishing'),
Expand Down
50 changes: 25 additions & 25 deletions app/lib/package/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -624,9 +624,9 @@ class PackageBackend {

/// Verifies an update to the credential-less publishing settings and
/// updates the Datastore entity if everything is valid.
Future<api.AutomatedPublishingConfig> setAutomatedPublishing(
Future<api.PkgPublishingConfig> setAutomatedPublishing(
String package,
api.AutomatedPublishingConfig body,
api.PkgPublishingConfig body,
) async {
final authenticatedUser = await requireAuthenticatedWebUser();
final user = authenticatedUser.user;
Expand Down Expand Up @@ -714,30 +714,30 @@ class PackageBackend {
}

// update lock
final current = p.automatedPublishing;
final current = p.publishingConfig;
final githubChanged =
(githubConfig?.isEnabled != current?.githubConfig?.isEnabled) ||
(githubConfig?.repository != current?.githubConfig?.repository);
if (githubChanged) {
p.automatedPublishing?.githubLock = null;
p.publishingConfig?.githubLock = null;
}
final gcpChanged =
(gcpConfig?.isEnabled != current?.gcpConfig?.isEnabled) ||
(gcpConfig?.serviceAccountEmail !=
current?.gcpConfig?.serviceAccountEmail);
if (gcpChanged) {
p.automatedPublishing?.gcpLock = null;
p.publishingConfig?.gcpLock = null;
}

// finalize changes
final automatedPublishing = p.automatedPublishing ??=
AutomatedPublishing();
automatedPublishing.githubConfig =
githubConfig ?? automatedPublishing.githubConfig;
automatedPublishing.gcpConfig =
gcpConfig ?? automatedPublishing.gcpConfig;
automatedPublishing.manualConfig =
manualConfig ?? automatedPublishing.manualConfig;
final publishingConfig = p.publishingConfig ?? PublishingConfig();
p.automatedPublishing = publishingConfig;
p.newPublishingConfig = publishingConfig;
publishingConfig.githubConfig =
githubConfig ?? publishingConfig.githubConfig;
publishingConfig.gcpConfig = gcpConfig ?? publishingConfig.gcpConfig;
publishingConfig.manualConfig =
manualConfig ?? publishingConfig.manualConfig;

p.updated = clock.now().toUtc();
tx.insert(p);
Expand All @@ -748,10 +748,10 @@ class PackageBackend {
user: user,
),
);
return api.AutomatedPublishingConfig(
github: p.automatedPublishing!.githubConfig,
gcp: p.automatedPublishing!.gcpConfig,
manual: p.automatedPublishing!.manualConfig,
return api.PkgPublishingConfig(
github: p.publishingConfig!.githubConfig,
gcp: p.publishingConfig!.gcpConfig,
manual: p.publishingConfig!.manualConfig,
);
});
}
Expand Down Expand Up @@ -1617,7 +1617,7 @@ class PackageBackend {
if (agent is AuthenticatedUser &&
await packageBackend.isPackageAdmin(package, agent.user.userId)) {
final isEnabled =
package.automatedPublishing?.manualConfig?.isEnabled ?? true;
package.publishingConfig?.manualConfig?.isEnabled ?? true;
if (!isEnabled) {
throw AuthorizationException.manualPublishingDisabled(package.name!);
}
Expand Down Expand Up @@ -1647,8 +1647,8 @@ class PackageBackend {
Package package,
String newVersion,
) async {
final githubConfig = package.automatedPublishing?.githubConfig;
final githubLock = package.automatedPublishing?.githubLock;
final githubConfig = package.publishingConfig?.githubConfig;
final githubLock = package.publishingConfig?.githubLock;

if (githubConfig?.isEnabled != true) {
throw AuthorizationException.githubActionIssue(
Expand Down Expand Up @@ -1722,7 +1722,7 @@ class PackageBackend {
);
await withRetryTransaction(db, (tx) async {
final p = await tx.lookupValue<Package>(package.key);
p.automatedPublishing!.githubConfig!.isEnabled = false;
p.publishingConfig!.githubConfig!.isEnabled = false;
tx.insert(p);
});
throw AuthorizationException.githubActionIssue(
Expand All @@ -1737,8 +1737,8 @@ class PackageBackend {
Package package,
String newVersion,
) async {
final gcpConfig = package.automatedPublishing?.gcpConfig;
final gcpLock = package.automatedPublishing?.gcpLock;
final gcpConfig = package.publishingConfig?.gcpConfig;
final gcpLock = package.publishingConfig?.gcpLock;
if (gcpConfig?.isEnabled != true) {
throw AuthorizationException.serviceAccountPublishingIssue(
'publishing with service account is not enabled',
Expand Down Expand Up @@ -1766,7 +1766,7 @@ class PackageBackend {
);
await withRetryTransaction(db, (tx) async {
final p = await tx.lookupValue<Package>(package.key);
p.automatedPublishing!.gcpConfig!.isEnabled = false;
p.publishingConfig!.gcpConfig!.isEnabled = false;
tx.insert(p);
});
throw AuthorizationException.githubActionIssue(
Expand Down Expand Up @@ -2008,7 +2008,7 @@ class PackageBackend {
Package package,
AuthenticatedAgent agent,
) {
final current = package.automatedPublishing;
final current = package.publishingConfig;
if (current == null) {
if (agent is AuthenticatedGitHubAction ||
agent is AuthenticatedGcpServiceAccount) {
Expand Down
38 changes: 21 additions & 17 deletions app/lib/package/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,15 @@ class Package extends db.ExpandoModel<String> {
@db.StringListProperty()
List<String>? deletedVersions;

/// Scheduling state for all versions of this package.
@AutomatedPublishingProperty()
AutomatedPublishing? automatedPublishing;
/// The configuration for automated and manual publishing.
@PublishingConfigProperty()
PublishingConfig? automatedPublishing;

@PublishingConfigProperty(propertyName: 'publishingConfig')
PublishingConfig? newPublishingConfig;

PublishingConfig? get publishingConfig =>
newPublishingConfig ?? automatedPublishing;

/// The latest point in time at which a security advisory that affects this
/// package has been synchronized into pub.
Expand Down Expand Up @@ -455,33 +461,31 @@ class Release {
}

@JsonSerializable(explicitToJson: true, includeIfNull: false)
class AutomatedPublishing {
class PublishingConfig {
GitHubPublishingConfig? githubConfig;
GitHubPublishingLock? githubLock;
GcpPublishingConfig? gcpConfig;
GcpPublishingLock? gcpLock;
ManualPublishingConfig? manualConfig;

AutomatedPublishing({
PublishingConfig({
this.githubConfig,
this.githubLock,
this.gcpConfig,
this.gcpLock,
this.manualConfig,
});

factory AutomatedPublishing.fromJson(Map<String, dynamic> json) =>
_$AutomatedPublishingFromJson(json);
factory PublishingConfig.fromJson(Map<String, dynamic> json) =>
_$PublishingConfigFromJson(json);

Map<String, dynamic> toJson() => _$AutomatedPublishingToJson(this);
Map<String, dynamic> toJson() => _$PublishingConfigToJson(this);
}

/// A [db.Property] encoding [AutomatedPublishing] as JSON.
class AutomatedPublishingProperty extends db.Property {
const AutomatedPublishingProperty({
super.propertyName,
super.required = false,
}) : super(indexed: false);
/// A [db.Property] encoding [PublishingConfig] as JSON.
class PublishingConfigProperty extends db.Property {
const PublishingConfigProperty({super.propertyName, super.required = false})
: super(indexed: false);

@override
Object? encodeValue(
Expand All @@ -490,21 +494,21 @@ class AutomatedPublishingProperty extends db.Property {
bool forComparison = false,
}) {
if (value == null) return null;
return json.encode(value as AutomatedPublishing);
return json.encode(value as PublishingConfig);
}

@override
Object? decodePrimitiveValue(db.ModelDB mdb, Object? value) {
if (value == null) return null;
return AutomatedPublishing.fromJson(
return PublishingConfig.fromJson(
json.decode(value as String) as Map<String, dynamic>,
);
}

@override
bool validate(db.ModelDB mdb, Object? value) =>
super.validate(mdb, value) &&
(value == null || value is AutomatedPublishing);
(value == null || value is PublishingConfig);
}

@JsonSerializable(explicitToJson: true, includeIfNull: false)
Expand Down
21 changes: 10 additions & 11 deletions app/lib/package/models.g.dart

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

13 changes: 12 additions & 1 deletion app/lib/tool/backfill/backfill_new_fields.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.

import 'package:logging/logging.dart';
import 'package:pub_dev/package/models.dart';
import 'package:pub_dev/shared/datastore.dart';

final _logger = Logger('backfill_new_fields');

Expand All @@ -12,5 +14,14 @@ final _logger = Logger('backfill_new_fields');
/// CHANGELOG.md must be updated with the new fields, and the next
/// release could remove the backfill from here.
Future<void> backfillNewFields() async {
_logger.info('Nothing to backfill.');
_logger.info('Backfill new Package.publishingConfig.');
await for (final p in dbService.query<Package>().run()) {
if (p.automatedPublishing != null && p.newPublishingConfig == null) {
await withRetryTransaction(dbService, (tx) async {
final pkg = await tx.lookupValue<Package>(p.key);
pkg.newPublishingConfig = pkg.automatedPublishing;
tx.insert(pkg);
});
}
}
}
Loading