Add REST API for migrating label functions to submit requirements This change adds two REST API endpoints: POST /projects/foo/migrate-labels POST /projects/foo/migrate-labels:review Both migrate label functions to submit-requirements. The first one commits the migration result into the refs/meta/config branch. The second one prepares a migration change for review. With these new endpoints, Gerrit admins can perform label function migration stepwise and involve project owners into the review process. Release-Notes: REST API for migrating label functions to submit requirements Change-Id: Iacfa133f96f20ab885b925fa38a981fde5ae9e4b
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt index 1fe0eff..f85f284 100644 --- a/Documentation/rest-api-projects.txt +++ b/Documentation/rest-api-projects.txt
@@ -4150,6 +4150,68 @@ } ---- +[[migrate-labels]] +=== Migrate label functions to submit requirements +-- +'POST /projects/link:#project-name[\{project-name\}]/migrate-labels' +-- + +Migrates labels with functions to submit requirements. The migration result is +committed into the `refs/meta/config` branch and thus immediately active. As a +response it returns link:#migrate-labels-info[MigrateLabelsInfo] entity +describing the outcome of the migration. + +The caller must be a project owner. + +.Request +---- + POST /projects/testproj/migrate-labels HTTP/1.0 + Content-Type: application/json; charset=UTF-8 +---- + +.Response +---- + HTTP/1.1 200 OK + Content-Type: application/json; charset=UTF-8 + + )]}' + {"status": "MIGRATED"} +---- + + +[[migrate-labels-change]] +=== Create change which migrate label functions to submit requirements +-- +'POST /projects/link:#project-name[\{project-name\}]/migrate-labels:review' +-- + +Creates a change for review which migrates labels with functions to submit requirements. +As a response it returns link:#migrate-labels-review-info[MigrageLabelsReviewInfo] entity +describing the outcome of the migration. + +.Request +---- + POST /projects/testproj/migrate-labels HTTP/1.0 + Content-Type: application/json; charset=UTF-8 +---- + +.Response +---- + HTTP/1.1 200 OK + Content-Type: application/json; charset=UTF-8 + + )]}' + { + "status": "MIGRATED", + "change": { + "id": "testproj~12345", + ... + } + } +---- + + + [[ids]] == IDs @@ -5266,6 +5328,40 @@ a date in the future. |========================= + +[[migrate-labels-info]] +=== MigrateLabelsInfo +The `MigrateLabelsInfo` entity contains information about an outcome of labels +function migration. + +[options="header",cols="1,^2,4"] +|============================= +|Field Name ||Description +|`status` ||The status of the migration. Takes one of the following values: +`MIGRATED`, +`HAS_PROLOG`, +`PREVIOUSLY_MIGRATED`, +`NO_CHANGE` +|============================= + +[[migrate-labels-review-info]] +=== MigrateLabelsReviewInfo +The `MigrateLabelsReviewInfo` entity contains information about an outcome of creating +a change for labels function migration. + +[options="header",cols="1,^2,4"] +|============================= +|Field Name ||Description +|`status` ||The status of the migration. Takes one of the following values: +`MIGRATED`, +`HAS_PROLOG`, +`PREVIOUSLY_MIGRATED`, +`NO_CHANGE` +|`change` |optional|The change created. +It is a link:rest-api-changes.html#change-info[ChangeInfo] entity +and is set only when the `status` value is `MIGRATED`. +|============================= + GERRIT ------ Part of link:index.html[Gerrit Code Review]
diff --git a/java/com/google/gerrit/server/restapi/BUILD b/java/com/google/gerrit/server/restapi/BUILD index 70ba6c5..47c6abe 100644 --- a/java/com/google/gerrit/server/restapi/BUILD +++ b/java/com/google/gerrit/server/restapi/BUILD
@@ -27,6 +27,7 @@ "//java/com/google/gerrit/server/flow", "//java/com/google/gerrit/server/ioutil", "//java/com/google/gerrit/server/logging", + "//java/com/google/gerrit/server/schema", "//java/com/google/gerrit/server/util/time", "//lib:args4j", "//lib:blame-cache",
diff --git a/java/com/google/gerrit/server/restapi/project/MigrateLabels.java b/java/com/google/gerrit/server/restapi/project/MigrateLabels.java new file mode 100644 index 0000000..3e889a3 --- /dev/null +++ b/java/com/google/gerrit/server/restapi/project/MigrateLabels.java
@@ -0,0 +1,83 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.restapi.project; + +import com.google.common.flogger.FluentLogger; +import com.google.gerrit.entities.Project; +import com.google.gerrit.extensions.restapi.Response; +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.server.permissions.PermissionBackend; +import com.google.gerrit.server.permissions.ProjectPermission; +import com.google.gerrit.server.project.ProjectResource; +import com.google.gerrit.server.schema.MigrateLabelFunctionsToSubmitRequirement; +import com.google.gerrit.server.schema.UpdateUI; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import java.util.Set; + +@Singleton +public class MigrateLabels implements RestModifyView<ProjectResource, MigrateLabelsInput> { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + private final MigrateLabelFunctionsToSubmitRequirement migrateLabelFunctionsToSubmitRequirement; + private final PermissionBackend permissionBackend; + + @Inject + MigrateLabels( + MigrateLabelFunctionsToSubmitRequirement migrateLabelFunctionsToSubmitRequirement, + PermissionBackend permissionBackend) { + this.migrateLabelFunctionsToSubmitRequirement = migrateLabelFunctionsToSubmitRequirement; + this.permissionBackend = permissionBackend; + } + + @Override + public Response<MigrateLabelsInfo> apply(ProjectResource rsrc, MigrateLabelsInput input) + throws Exception { + Project.NameKey project = rsrc.getNameKey(); + permissionBackend.currentUser().project(project).check(ProjectPermission.WRITE_CONFIG); + MigrateLabelFunctionsToSubmitRequirement.Status status = + migrateLabelFunctionsToSubmitRequirement.executeMigration(project, new LoggingUpdateUI()); + + MigrateLabelsInfo info = new MigrateLabelsInfo(); + info.status = status; + return Response.ok(info); + } + + public static class LoggingUpdateUI implements UpdateUI { + + @Override + public void message(String message) { + logger.atInfo().log(message); + } + + @Override + public boolean yesno(boolean defaultValue, String message) { + return false; + } + + @Override + public void waitForUser() {} + + @Override + public String readString(String defaultValue, Set<String> allowedValues, String message) { + return null; + } + + @Override + public boolean isBatch() { + return false; + } + } +}
diff --git a/java/com/google/gerrit/server/restapi/project/MigrateLabelsInfo.java b/java/com/google/gerrit/server/restapi/project/MigrateLabelsInfo.java new file mode 100644 index 0000000..b6a2920 --- /dev/null +++ b/java/com/google/gerrit/server/restapi/project/MigrateLabelsInfo.java
@@ -0,0 +1,21 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.restapi.project; + +import com.google.gerrit.server.schema.MigrateLabelFunctionsToSubmitRequirement; + +public class MigrateLabelsInfo { + public MigrateLabelFunctionsToSubmitRequirement.Status status; +}
diff --git a/java/com/google/gerrit/server/restapi/project/MigrateLabelsInput.java b/java/com/google/gerrit/server/restapi/project/MigrateLabelsInput.java new file mode 100644 index 0000000..d010a9d --- /dev/null +++ b/java/com/google/gerrit/server/restapi/project/MigrateLabelsInput.java
@@ -0,0 +1,17 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.restapi.project; + +public class MigrateLabelsInput {}
diff --git a/java/com/google/gerrit/server/restapi/project/MigrateLabelsReview.java b/java/com/google/gerrit/server/restapi/project/MigrateLabelsReview.java new file mode 100644 index 0000000..0b31d63 --- /dev/null +++ b/java/com/google/gerrit/server/restapi/project/MigrateLabelsReview.java
@@ -0,0 +1,62 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.restapi.project; + +import static com.google.gerrit.server.schema.MigrateLabelFunctionsToSubmitRequirement.Status.MIGRATED; + +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.ResourceConflictException; +import com.google.gerrit.extensions.restapi.Response; +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.server.project.ProjectResource; +import com.google.gerrit.server.project.RepoMetaDataUpdater; +import com.google.gerrit.server.project.RepoMetaDataUpdater.ConfigChangeCreator; +import com.google.gerrit.server.schema.MigrateLabelFunctionsToSubmitRequirement; +import com.google.inject.Inject; +import com.google.inject.Singleton; + +@Singleton +public class MigrateLabelsReview implements RestModifyView<ProjectResource, MigrateLabelsInput> { + + private final RepoMetaDataUpdater repoMetaDataUpdater; + private final MigrateLabelFunctionsToSubmitRequirement migrateLabelFunctionsToSubmitRequirement; + + @Inject + MigrateLabelsReview( + RepoMetaDataUpdater repoMetaDataUpdater, + MigrateLabelFunctionsToSubmitRequirement migrateLabelFunctionsToSubmitRequirement) { + this.repoMetaDataUpdater = repoMetaDataUpdater; + this.migrateLabelFunctionsToSubmitRequirement = migrateLabelFunctionsToSubmitRequirement; + } + + @Override + public Response<MigrateLabelsReviewInfo> apply(ProjectResource rsrc, MigrateLabelsInput input) + throws AuthException, BadRequestException, ResourceConflictException, Exception { + try (ConfigChangeCreator creator = + repoMetaDataUpdater.configChangeCreator( + rsrc.getNameKey(), null, MigrateLabelFunctionsToSubmitRequirement.COMMIT_MSG)) { + MigrateLabelFunctionsToSubmitRequirement.Status status = + migrateLabelFunctionsToSubmitRequirement.updateConfig( + rsrc.getProjectState().getNameKey(), + creator.getConfig(), + new MigrateLabels.LoggingUpdateUI()); + if (status == MIGRATED) { + return Response.ok(new MigrateLabelsReviewInfo(MIGRATED, creator.createChange().value())); + } + return Response.ok(new MigrateLabelsReviewInfo(status)); + } + } +}
diff --git a/java/com/google/gerrit/server/restapi/project/MigrateLabelsReviewInfo.java b/java/com/google/gerrit/server/restapi/project/MigrateLabelsReviewInfo.java new file mode 100644 index 0000000..01d9640 --- /dev/null +++ b/java/com/google/gerrit/server/restapi/project/MigrateLabelsReviewInfo.java
@@ -0,0 +1,33 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.restapi.project; + +import com.google.gerrit.extensions.common.ChangeInfo; +import com.google.gerrit.server.schema.MigrateLabelFunctionsToSubmitRequirement; + +public class MigrateLabelsReviewInfo { + public MigrateLabelFunctionsToSubmitRequirement.Status status; + public ChangeInfo change; + + public MigrateLabelsReviewInfo( + MigrateLabelFunctionsToSubmitRequirement.Status status, ChangeInfo change) { + this.status = status; + this.change = change; + } + + public MigrateLabelsReviewInfo(MigrateLabelFunctionsToSubmitRequirement.Status status) { + this(status, null); + } +}
diff --git a/java/com/google/gerrit/server/restapi/project/ProjectRestApiModule.java b/java/com/google/gerrit/server/restapi/project/ProjectRestApiModule.java index f5647ec..adba60e 100644 --- a/java/com/google/gerrit/server/restapi/project/ProjectRestApiModule.java +++ b/java/com/google/gerrit/server/restapi/project/ProjectRestApiModule.java
@@ -87,6 +87,9 @@ put(PROJECT_KIND, "config").to(PutConfig.class); put(PROJECT_KIND, "config:review").to(PutConfigReview.class); + post(PROJECT_KIND, "migrate-labels").to(MigrateLabels.class); + post(PROJECT_KIND, "migrate-labels:review").to(MigrateLabelsReview.class); + post(PROJECT_KIND, "create.change").to(CreateChange.class); child(PROJECT_KIND, "dashboards").to(DashboardsCollection.class);
diff --git a/java/com/google/gerrit/server/schema/MigrateLabelFunctionsToSubmitRequirement.java b/java/com/google/gerrit/server/schema/MigrateLabelFunctionsToSubmitRequirement.java index 37d9802..bdc521e 100644 --- a/java/com/google/gerrit/server/schema/MigrateLabelFunctionsToSubmitRequirement.java +++ b/java/com/google/gerrit/server/schema/MigrateLabelFunctionsToSubmitRequirement.java
@@ -135,7 +135,7 @@ } } - private Status updateConfig(Project.NameKey project, ProjectConfig projectConfig, UpdateUI ui) + public Status updateConfig(Project.NameKey project, ProjectConfig projectConfig, UpdateUI ui) throws IOException { boolean updated = false; if (hasPrologRules(project)) {