blob: 93fc84c2d64310803c5e46c98ef7ff5f451b6820 [file] [log] [blame]
// Copyright (C) 2014 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.googlesource.gerrit.plugins.serviceuser;
import static com.googlesource.gerrit.plugins.serviceuser.CreateServiceUser.KEY_CREATED_BY;
import static com.googlesource.gerrit.plugins.serviceuser.CreateServiceUser.KEY_OWNER;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.git.NotesBranchUtil;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.googlesource.gerrit.plugins.serviceuser.GetServiceUser.ServiceUserInfo;
import java.io.IOException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.notes.NoteMap;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class CreateServiceUserNotes {
private static final Logger log = LoggerFactory.getLogger(CreateServiceUserNotes.class);
interface Factory {
CreateServiceUserNotes create(Project.NameKey project, Repository git);
}
private static final String REFS_NOTES_SERVICEUSER = "refs/notes/serviceuser";
private final PersonIdent gerritServerIdent;
private final NotesBranchUtil.Factory notesBranchUtilFactory;
private final ServiceUserResolver serviceUserResolver;
private final @AnonymousCowardName String anonymousCowardName;
private final Project.NameKey project;
private final Repository git;
private ObjectInserter inserter;
private NoteMap serviceUserNotes;
private StringBuilder message;
@Inject
CreateServiceUserNotes(
@GerritPersonIdent PersonIdent gerritIdent,
NotesBranchUtil.Factory notesBranchUtilFactory,
ServiceUserResolver serviceUserResolver,
@AnonymousCowardName String anonymousCowardName,
@Assisted Project.NameKey project,
@Assisted Repository git) {
this.gerritServerIdent = gerritIdent;
this.notesBranchUtilFactory = notesBranchUtilFactory;
this.serviceUserResolver = serviceUserResolver;
this.anonymousCowardName = anonymousCowardName;
this.project = project;
this.git = git;
}
void createNotes(String branch, ObjectId oldObjectId, ObjectId newObjectId)
throws IOException, RestApiException {
if (ObjectId.zeroId().equals(newObjectId)) {
return;
}
try (RevWalk rw = new RevWalk(git)) {
try {
RevObject obj = rw.peel(rw.parseAny(newObjectId));
if (obj.getType() != OBJ_COMMIT) {
return;
}
RevCommit n = (RevCommit) obj;
rw.markStart(n);
if (n.getParentCount() == 1 && n.getParent(0).equals(oldObjectId)) {
rw.markUninteresting(rw.parseCommit(oldObjectId));
} else {
markUninteresting(git, branch, rw, oldObjectId);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
return;
}
for (RevCommit c : rw) {
ServiceUserInfo serviceUser = serviceUserResolver.getAsServiceUser(c.getCommitterIdent());
if (serviceUser != null) {
ObjectId content = createNoteContent(branch, serviceUser);
getNotes().set(c, content);
getMessage().append("* ").append(c.getShortMessage()).append("\n");
}
}
}
}
void commitNotes() throws IOException {
try {
if (serviceUserNotes == null) {
return;
}
message.insert(0, "Update notes for service user commits\n\n");
notesBranchUtilFactory
.create(project, git, inserter)
.commitAllNotes(
serviceUserNotes, REFS_NOTES_SERVICEUSER, gerritServerIdent, message.toString());
} finally {
if (inserter != null) {
inserter.close();
}
}
}
private void markUninteresting(Repository git, String branch, RevWalk rw, ObjectId oldObjectId) {
for (Ref r : git.getAllRefs().values()) {
try {
if (r.getName().equals(branch)) {
if (!ObjectId.zeroId().equals(oldObjectId)) {
// For the updated branch the oldObjectId is the tip of uninteresting
// commit history
rw.markUninteresting(rw.parseCommit(oldObjectId));
}
} else if (r.getName().startsWith(Constants.R_HEADS)
|| r.getName().startsWith(Constants.R_TAGS)) {
rw.markUninteresting(rw.parseCommit(r.getObjectId()));
}
} catch (IOException e) {
// skip if not parseable as a commit
}
}
}
private ObjectId createNoteContent(String branch, ServiceUserInfo serviceUser)
throws IOException, RestApiException, RuntimeException {
return getInserter()
.insert(Constants.OBJ_BLOB, createServiceUserNote(branch, serviceUser).getBytes(UTF_8));
}
private String createServiceUserNote(String branch, ServiceUserInfo serviceUser)
throws RestApiException, RuntimeException {
HeaderFormatter fmt = new HeaderFormatter(gerritServerIdent.getTimeZone(), anonymousCowardName);
fmt.appendDate();
fmt.append("Project", project.get());
fmt.append("Branch", branch);
fmt.appendUser(KEY_CREATED_BY, serviceUser.createdBy);
for (AccountInfo owner : serviceUserResolver.listActiveOwners(serviceUser)) {
fmt.appendUser(KEY_OWNER, owner);
}
return fmt.toString();
}
private ObjectInserter getInserter() {
if (inserter == null) {
inserter = git.newObjectInserter();
}
return inserter;
}
private NoteMap getNotes() {
if (serviceUserNotes == null) {
serviceUserNotes = NoteMap.newEmptyMap();
}
return serviceUserNotes;
}
private StringBuilder getMessage() {
if (message == null) {
message = new StringBuilder();
}
return message;
}
}