Merge branch 'stable-3.1'
* stable-3.1: Replace documentation of gerrit.ui with gerrit.enableGwtUi Use the environment variables provided by the GerritCodeReview-plugin DeleteZombieCommentsRefsTest: Open RevWalk in try-with-resource Documentation: Mention unused_deps with buildifier Bazel: Remove bazel version check Documentation: Recommend Bazelisk to launch bazel Add a pgm to delete dead draft comments refs Change-Id: I78758c7168b7374b491413dcc7f130df3d9dfd58
This commit is contained in:
commit
bfe2d146e1
@ -22,12 +22,22 @@ To build Gerrit from source, you need:
|
||||
* Python 2 or 3
|
||||
* link:https://github.com/nodesource/distributions/blob/master/README.md[Node.js (including npm),role=external,window=_blank]
|
||||
* Bower (`sudo npm install -g bower`)
|
||||
* link:https://docs.bazel.build/versions/master/install.html[Bazel,role=external,window=_blank] directly
|
||||
or through link:https://github.com/bazelbuild/bazelisk[Bazelisk,role=external,window=_blank]
|
||||
* link:https://docs.bazel.build/versions/master/install.html[Bazel,role=external,window=_blank] -launched with
|
||||
link:https://github.com/bazelbuild/bazelisk[Bazelisk,role=external,window=_blank]
|
||||
* Maven
|
||||
* zip, unzip
|
||||
* gcc
|
||||
|
||||
[[bazel]]
|
||||
=== Bazel
|
||||
|
||||
link:https://github.com/bazelbuild/bazelisk[Bazelisk,role=external,window=_blank] includes a
|
||||
link:https://bazel.build/[Bazel,role=external,window=_blank] version check and downloads the correct
|
||||
`bazel` version for the git project/repository. Bazelisk is the recommended
|
||||
`bazel` launcher for Gerrit. Once Bazelisk is installed locally, a `bazel`
|
||||
symlink can be created towards it. This is so that every `bazel` command
|
||||
seamlessly uses Bazelisk, which then runs the proper `bazel` binary version.
|
||||
|
||||
[[java]]
|
||||
=== Java
|
||||
|
||||
|
@ -116,7 +116,10 @@ To format Java source code, Gerrit uses the
|
||||
link:https://github.com/google/google-java-format[`google-java-format`,role=external,window=_blank]
|
||||
tool (version 1.7), and to format Bazel BUILD, WORKSPACE and .bzl files the
|
||||
link:https://github.com/bazelbuild/buildtools/tree/master/buildifier[`buildifier`,role=external,window=_blank]
|
||||
tool (version 0.29.0).
|
||||
tool (version 0.29.0). Unused dependencies are found and removed using the
|
||||
link:https://github.com/bazelbuild/buildtools/tree/master/unused_deps[`unused_deps`,role=external,window=_blank]
|
||||
build tool, a sibling of `buildifier`.
|
||||
|
||||
These tools automatically apply format according to the style guides; this
|
||||
streamlines code review by reducing the need for time-consuming, tedious,
|
||||
and contentious discussions about trivial issues like whitespace.
|
||||
|
@ -2,7 +2,7 @@
|
||||
= Gerrit Code Review: Developer Setup
|
||||
|
||||
To build a developer instance, you'll need link:https://bazel.build/[Bazel,role=external,window=_blank] to
|
||||
compile the code.
|
||||
compile the code, preferably launched with link:https://github.com/bazelbuild/bazelisk[Bazelisk,role=external,window=_blank].
|
||||
|
||||
== Git Setup
|
||||
|
||||
|
44
Jenkinsfile
vendored
44
Jenkinsfile
vendored
@ -25,15 +25,6 @@ class Globals {
|
||||
static final String gerritRepositoryNameSha1Suffix = "-a6a0e4682515f3521897c5f950d1394f4619d928"
|
||||
}
|
||||
|
||||
class Change {
|
||||
static String sha1 = ""
|
||||
static String number = ""
|
||||
static String branch = ""
|
||||
static String ref = ""
|
||||
static String patchNum = ""
|
||||
static String url = ""
|
||||
}
|
||||
|
||||
class Build {
|
||||
String url
|
||||
String result
|
||||
@ -83,36 +74,17 @@ def postCheck(check) {
|
||||
gerritCheck(checks: [ "${check.uuid}" : "${check.getCheckResultFromBuild()}" ], url: "${check.consoleUrl}")
|
||||
}
|
||||
|
||||
def queryChangedFiles(url, changeNum, sha1) {
|
||||
def queryUrl = "${url}changes/${Change.number}/revisions/${Change.sha1}/files/"
|
||||
def queryChangedFiles(url) {
|
||||
def queryUrl = "${url}changes/${env.GERRIT_CHANGE_NUMBER}/revisions/${env.GERRIT_PATCHSET_REVISION}/files/"
|
||||
def response = httpRequest queryUrl
|
||||
def files = response.getContent().substring(5)
|
||||
def filesJson = new JsonSlurper().parseText(files)
|
||||
return filesJson.keySet().findAll { it != "/COMMIT_MSG" }
|
||||
}
|
||||
|
||||
def queryChange(){
|
||||
def requestedChangeId = env.BRANCH_NAME.split('/')[1]
|
||||
def queryUrl = "${Globals.gerritUrl}changes/${requestedChangeId}/?pp=0&O=3"
|
||||
def response = httpRequest queryUrl
|
||||
def jsonSlurper = new JsonSlurper()
|
||||
return jsonSlurper.parseText(response.getContent().substring(5))
|
||||
}
|
||||
|
||||
def getChangeMetaData(){
|
||||
def changeJson = queryChange()
|
||||
Change.sha1 = changeJson.current_revision
|
||||
Change.number = changeJson._number
|
||||
Change.branch = changeJson.branch
|
||||
def revision = changeJson.revisions.get(Change.sha1)
|
||||
Change.ref = revision.ref
|
||||
Change.patchNum = revision._number
|
||||
Change.url = Globals.gerritUrl + "#/c/" + Change.number + "/" + Change.patchNum
|
||||
}
|
||||
|
||||
def collectBuildModes() {
|
||||
Builds.modes = ["notedb"]
|
||||
def changedFiles = queryChangedFiles(Globals.gerritUrl, Change.number, Change.sha1)
|
||||
def changedFiles = queryChangedFiles(Globals.gerritUrl)
|
||||
def polygerritFiles = changedFiles.findAll { it.startsWith("polygerrit-ui") ||
|
||||
it.startsWith("lib/js") }
|
||||
|
||||
@ -137,11 +109,11 @@ def prepareBuildsForMode(buildName, mode="notedb", retryTimes = 1) {
|
||||
for (int i = 1; i <= retryTimes; i++) {
|
||||
try {
|
||||
slaveBuild = build job: "${buildName}", parameters: [
|
||||
string(name: 'REFSPEC', value: Change.ref),
|
||||
string(name: 'BRANCH', value: Change.sha1),
|
||||
string(name: 'CHANGE_URL', value: Change.url),
|
||||
string(name: 'REFSPEC', value: "refs/changes/${env.BRANCH_NAME}"),
|
||||
string(name: 'BRANCH', value: env.GERRIT_PATCHSET_REVISION),
|
||||
string(name: 'CHANGE_URL', value: "${Globals.gerritUrl}c/${env.GERRIT_PROJECT}/+/${env.GERRIT_CHANGE_NUMBER}"),
|
||||
string(name: 'MODE', value: mode),
|
||||
string(name: 'TARGET_BRANCH', value: Change.branch)
|
||||
string(name: 'TARGET_BRANCH', value: env.GERRIT_BRANCH)
|
||||
], propagate: false
|
||||
} finally {
|
||||
if (buildName == "Gerrit-codestyle"){
|
||||
@ -243,8 +215,6 @@ node ('master') {
|
||||
if (hasChangeNumber()) {
|
||||
stage('Preparing'){
|
||||
gerritReview labels: ['Verified': 0, 'Code-Style': 0]
|
||||
|
||||
getChangeMetaData()
|
||||
collectBuildModes()
|
||||
}
|
||||
}
|
||||
|
17
WORKSPACE
17
WORKSPACE
@ -23,13 +23,6 @@ load("@bazel_toolchains//rules:rbe_repo.bzl", "rbe_autoconfig")
|
||||
# otherwise refer to RBE docs.
|
||||
rbe_autoconfig(name = "rbe_default")
|
||||
|
||||
http_archive(
|
||||
name = "bazel_skylib",
|
||||
sha256 = "2ea8a5ed2b448baf4a6855d3ce049c4c452a6470b1efd1504fdb7c1c134d220a",
|
||||
strip_prefix = "bazel-skylib-0.8.0",
|
||||
urls = ["https://github.com/bazelbuild/bazel-skylib/archive/0.8.0.tar.gz"],
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_closure",
|
||||
sha256 = "03c3b16f205085817fd89cfdcb2220a0138647ee7992be9cef291b069dd90301",
|
||||
@ -47,15 +40,6 @@ http_file(
|
||||
urls = ["https://raw.githubusercontent.com/google/closure-compiler/35d2b3340ff23a69441f10fa3bc820691c2942f2/contrib/externs/polymer-1.0.js"],
|
||||
)
|
||||
|
||||
# Check Bazel version when invoked by Bazel directly
|
||||
load("//tools/bzl:bazelisk_version.bzl", "bazelisk_version")
|
||||
|
||||
bazelisk_version(name = "bazelisk_version")
|
||||
|
||||
load("@bazelisk_version//:check.bzl", "check_bazel_version")
|
||||
|
||||
check_bazel_version()
|
||||
|
||||
load("@io_bazel_rules_closure//closure:repositories.bzl", "rules_closure_dependencies", "rules_closure_toolchains")
|
||||
|
||||
# Prevent redundant loading of dependencies.
|
||||
@ -64,7 +48,6 @@ load("@io_bazel_rules_closure//closure:repositories.bzl", "rules_closure_depende
|
||||
# https://github.com/google/closure-templates/pull/155
|
||||
rules_closure_dependencies(
|
||||
omit_aopalliance = True,
|
||||
omit_bazel_skylib = True,
|
||||
omit_javax_inject = True,
|
||||
omit_rules_cc = True,
|
||||
)
|
||||
|
78
java/com/google/gerrit/pgm/DeleteZombieDrafts.java
Normal file
78
java/com/google/gerrit/pgm/DeleteZombieDrafts.java
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright (C) 2020 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.pgm;
|
||||
|
||||
import com.google.gerrit.pgm.init.api.ConsoleUI;
|
||||
import com.google.gerrit.pgm.util.SiteProgram;
|
||||
import com.google.gerrit.server.config.GerritServerConfigModule;
|
||||
import com.google.gerrit.server.config.SitePath;
|
||||
import com.google.gerrit.server.notedb.DeleteZombieCommentsRefs;
|
||||
import com.google.gerrit.server.notedb.DeleteZombieCommentsRefs.Factory;
|
||||
import com.google.gerrit.server.schema.SchemaModule;
|
||||
import com.google.gerrit.server.securestore.SecureStoreClassName;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||
import com.google.inject.util.Providers;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
/**
|
||||
* A pgm which can be used to clean zombie draft comments refs More context in
|
||||
* https://gerrit-review.googlesource.com/c/gerrit/+/246233
|
||||
*
|
||||
* <p>The implementation is in {@link DeleteZombieCommentsRefs}
|
||||
*/
|
||||
public class DeleteZombieDrafts extends SiteProgram {
|
||||
@Option(
|
||||
name = "--cleanup-percentage",
|
||||
aliases = {"-c"},
|
||||
usage = "Clean a % of zombie drafts (default is 100%)")
|
||||
private Integer cleanupPercentage = 100;
|
||||
|
||||
@Override
|
||||
public int run() throws IOException {
|
||||
mustHaveValidSite();
|
||||
Injector sysInjector = getSysInjector();
|
||||
DeleteZombieCommentsRefs cleanup =
|
||||
sysInjector.getInstance(Factory.class).create(cleanupPercentage);
|
||||
cleanup.execute();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Injector getSysInjector() {
|
||||
List<Module> modules = new ArrayList<>();
|
||||
modules.add(
|
||||
new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(Path.class).annotatedWith(SitePath.class).toInstance(getSitePath());
|
||||
bind(ConsoleUI.class).toInstance(ConsoleUI.getInstance(false));
|
||||
bind(String.class)
|
||||
.annotatedWith(SecureStoreClassName.class)
|
||||
.toProvider(Providers.of(getConfiguredSecureStoreClass()));
|
||||
install(new FactoryModuleBuilder().build(DeleteZombieCommentsRefs.Factory.class));
|
||||
}
|
||||
});
|
||||
modules.add(new GerritServerConfigModule());
|
||||
modules.add(new SchemaModule());
|
||||
return Guice.createInjector(modules);
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
// Copyright (C) 2020 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.notedb;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gerrit.entities.Change;
|
||||
import com.google.gerrit.git.RefUpdateUtil;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.eclipse.jgit.lib.BatchRefUpdate;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.transport.ReceiveCommand;
|
||||
|
||||
/**
|
||||
* This class can be used to clean zombie draft comments refs. More context in <a
|
||||
* href="https://gerrit-review.googlesource.com/c/gerrit/+/246233">
|
||||
* https://gerrit-review.googlesource.com/c/gerrit/+/246233 </a>
|
||||
*
|
||||
* <p>An earlier bug in the deletion of draft comments {@code
|
||||
* refs/draft-comments/$change_id_short/$change_id/$user_id} caused some draft refs to remain in Git
|
||||
* and not get deleted. These refs point to an empty tree.
|
||||
*/
|
||||
public class DeleteZombieCommentsRefs {
|
||||
private final String EMPTY_TREE_ID = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
||||
private final String DRAFT_REFS_PREFIX = "refs/draft-comments";
|
||||
private final int CHUNK_SIZE = 100; // log progress after deleting every CHUNK_SIZE refs
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final AllUsersName allUsers;
|
||||
private final int cleanupPercentage;
|
||||
private Repository allUsersRepo;
|
||||
|
||||
public interface Factory {
|
||||
DeleteZombieCommentsRefs create(int cleanupPercentage);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public DeleteZombieCommentsRefs(
|
||||
AllUsersName allUsers,
|
||||
GitRepositoryManager repoManager,
|
||||
@Assisted Integer cleanupPercentage) {
|
||||
this.allUsers = allUsers;
|
||||
this.repoManager = repoManager;
|
||||
this.cleanupPercentage = (cleanupPercentage == null) ? 100 : cleanupPercentage;
|
||||
}
|
||||
|
||||
public void execute() throws IOException {
|
||||
allUsersRepo = repoManager.openRepository(allUsers);
|
||||
|
||||
List<Ref> draftRefs = allUsersRepo.getRefDatabase().getRefsByPrefix(DRAFT_REFS_PREFIX);
|
||||
List<Ref> zombieRefs = filterZombieRefs(draftRefs);
|
||||
|
||||
System.out.println(
|
||||
String.format(
|
||||
"Found a total of %d zombie draft refs in %s repo.",
|
||||
zombieRefs.size(), allUsers.get()));
|
||||
|
||||
System.out.println(String.format("Cleanup percentage = %d", cleanupPercentage));
|
||||
zombieRefs =
|
||||
zombieRefs.stream()
|
||||
.filter(ref -> Change.Id.fromAllUsersRef(ref.getName()).get() % 100 < cleanupPercentage)
|
||||
.collect(toImmutableList());
|
||||
System.out.println(
|
||||
String.format("Number of zombie refs to be cleaned = %d", zombieRefs.size()));
|
||||
|
||||
long zombieRefsCnt = zombieRefs.size();
|
||||
long deletedRefsCnt = 0;
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
for (List<Ref> refsBatch : Iterables.partition(zombieRefs, CHUNK_SIZE)) {
|
||||
deleteBatchZombieRefs(refsBatch);
|
||||
long elapsed = (System.currentTimeMillis() - startTime) / 1000;
|
||||
deletedRefsCnt += refsBatch.size();
|
||||
logProgress(deletedRefsCnt, zombieRefsCnt, elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteBatchZombieRefs(List<Ref> refsBatch) throws IOException {
|
||||
List<ReceiveCommand> deleteCommands =
|
||||
refsBatch.stream()
|
||||
.map(
|
||||
zombieRef ->
|
||||
new ReceiveCommand(
|
||||
zombieRef.getObjectId(), ObjectId.zeroId(), zombieRef.getName()))
|
||||
.collect(toImmutableList());
|
||||
BatchRefUpdate bru = allUsersRepo.getRefDatabase().newBatchUpdate();
|
||||
bru.setAtomic(true);
|
||||
bru.addCommand(deleteCommands);
|
||||
RefUpdateUtil.executeChecked(bru, allUsersRepo);
|
||||
}
|
||||
|
||||
private List<Ref> filterZombieRefs(List<Ref> allDraftRefs) throws IOException {
|
||||
List<Ref> zombieRefs = new ArrayList<>((int) (allDraftRefs.size() * 0.5));
|
||||
for (Ref ref : allDraftRefs) {
|
||||
if (isZombieRef(ref)) {
|
||||
zombieRefs.add(ref);
|
||||
}
|
||||
}
|
||||
return zombieRefs;
|
||||
}
|
||||
|
||||
private boolean isZombieRef(Ref ref) throws IOException {
|
||||
return allUsersRepo.parseCommit(ref.getObjectId()).getTree().getName().equals(EMPTY_TREE_ID);
|
||||
}
|
||||
|
||||
private void logProgress(long deletedRefsCount, long allRefsCount, long elapsed) {
|
||||
System.out.format(
|
||||
"Deleted %d/%d zombie draft refs (%d seconds)\n", deletedRefsCount, allRefsCount, elapsed);
|
||||
}
|
||||
}
|
@ -0,0 +1,247 @@
|
||||
// Copyright (C) 2020 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.git;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.gerrit.entities.Account;
|
||||
import com.google.gerrit.entities.Change;
|
||||
import com.google.gerrit.entities.Project;
|
||||
import com.google.gerrit.entities.RefNames;
|
||||
import com.google.gerrit.git.RefUpdateUtil;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.notedb.DeleteZombieCommentsRefs;
|
||||
import com.google.gerrit.server.util.time.TimeUtil;
|
||||
import com.google.gerrit.testing.InMemoryRepositoryManager;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.eclipse.jgit.lib.BatchRefUpdate;
|
||||
import org.eclipse.jgit.lib.CommitBuilder;
|
||||
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.RefUpdate;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.lib.TreeFormatter;
|
||||
import org.eclipse.jgit.revwalk.RevBlob;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.transport.ReceiveCommand;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class DeleteZombieCommentsRefsTest {
|
||||
private InMemoryRepositoryManager repoManager = new InMemoryRepositoryManager();
|
||||
private Project.NameKey allUsersProject = Project.nameKey("All-Users");
|
||||
|
||||
@Test
|
||||
public void cleanZombieDraftsSmall() throws Exception {
|
||||
try (Repository usersRepo = repoManager.createRepository(allUsersProject)) {
|
||||
Ref ref1 = createRefWithNonEmptyTreeCommit(usersRepo, 1, 1000001);
|
||||
Ref ref2 = createRefWithEmptyTreeCommit(usersRepo, 1, 1000002);
|
||||
|
||||
DeleteZombieCommentsRefs clean =
|
||||
new DeleteZombieCommentsRefs(new AllUsersName("All-Users"), repoManager, null);
|
||||
clean.execute();
|
||||
|
||||
/* Check that ref1 still exists, and ref2 is deleted */
|
||||
assertThat(usersRepo.exactRef(ref1.getName())).isNotNull();
|
||||
assertThat(usersRepo.exactRef(ref2.getName())).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanZombieDraftsWithPercentage() throws Exception {
|
||||
try (Repository usersRepo = repoManager.createRepository(allUsersProject)) {
|
||||
Ref ref1 = createRefWithNonEmptyTreeCommit(usersRepo, 1005, 1000001);
|
||||
Ref ref2 = createRefWithEmptyTreeCommit(usersRepo, 1006, 1000002);
|
||||
Ref ref3 = createRefWithEmptyTreeCommit(usersRepo, 1060, 1000002);
|
||||
|
||||
assertThat(usersRepo.getRefDatabase().getRefs()).hasSize(3);
|
||||
|
||||
int cleanupPercentage = 50;
|
||||
DeleteZombieCommentsRefs clean =
|
||||
new DeleteZombieCommentsRefs(
|
||||
new AllUsersName("All-Users"), repoManager, cleanupPercentage);
|
||||
clean.execute();
|
||||
|
||||
/* ref1 not deleted, ref2 deleted, ref3 not deleted because of the clean percentage */
|
||||
assertThat(usersRepo.getRefDatabase().getRefs()).hasSize(2);
|
||||
assertThat(usersRepo.exactRef(ref1.getName())).isNotNull();
|
||||
assertThat(usersRepo.exactRef(ref2.getName())).isNull();
|
||||
assertThat(usersRepo.exactRef(ref3.getName())).isNotNull();
|
||||
|
||||
/* Re-execute the cleanup and make sure nothing's changed */
|
||||
clean.execute();
|
||||
assertThat(usersRepo.getRefDatabase().getRefs()).hasSize(2);
|
||||
assertThat(usersRepo.exactRef(ref1.getName())).isNotNull();
|
||||
assertThat(usersRepo.exactRef(ref2.getName())).isNull();
|
||||
assertThat(usersRepo.exactRef(ref3.getName())).isNotNull();
|
||||
|
||||
/* Increase the cleanup percentage */
|
||||
cleanupPercentage = 70;
|
||||
clean =
|
||||
new DeleteZombieCommentsRefs(
|
||||
new AllUsersName("All-Users"), repoManager, cleanupPercentage);
|
||||
|
||||
clean.execute();
|
||||
|
||||
/* Now ref3 is deleted */
|
||||
assertThat(usersRepo.getRefDatabase().getRefs()).hasSize(1);
|
||||
assertThat(usersRepo.exactRef(ref1.getName())).isNotNull();
|
||||
assertThat(usersRepo.exactRef(ref2.getName())).isNull();
|
||||
assertThat(usersRepo.exactRef(ref3.getName())).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanZombieDraftsLarge() throws Exception {
|
||||
try (Repository usersRepo = repoManager.createRepository(allUsersProject)) {
|
||||
int goodRefsCnt = 5000;
|
||||
int zombieRefsCnt = 5000;
|
||||
int userIdGoodRefs = 1000001;
|
||||
int userIdBadRefs = 1000002;
|
||||
|
||||
Ref nonEmptyBaseRef = createRefWithNonEmptyTreeCommit(usersRepo, 1, userIdGoodRefs);
|
||||
Ref emptyBaseRef = createRefWithEmptyTreeCommit(usersRepo, 1, userIdBadRefs);
|
||||
|
||||
List<String> goodRefs =
|
||||
createNRefsOnCommit(
|
||||
usersRepo, nonEmptyBaseRef.getObjectId(), goodRefsCnt, userIdGoodRefs);
|
||||
List<String> badRefs =
|
||||
createNRefsOnCommit(usersRepo, emptyBaseRef.getObjectId(), zombieRefsCnt, userIdBadRefs);
|
||||
|
||||
goodRefs.add(0, nonEmptyBaseRef.getName());
|
||||
badRefs.add(0, emptyBaseRef.getName());
|
||||
|
||||
assertThat(usersRepo.getRefDatabase().getRefs().size())
|
||||
.isEqualTo(goodRefs.size() + badRefs.size());
|
||||
|
||||
DeleteZombieCommentsRefs clean =
|
||||
new DeleteZombieCommentsRefs(new AllUsersName("All-Users"), repoManager, null);
|
||||
clean.execute();
|
||||
|
||||
assertThat(
|
||||
usersRepo.getRefDatabase().getRefs().stream()
|
||||
.map(Ref::getName)
|
||||
.collect(toImmutableList()))
|
||||
.containsExactlyElementsIn(goodRefs);
|
||||
|
||||
assertThat(
|
||||
usersRepo.getRefDatabase().getRefs().stream()
|
||||
.map(Ref::getName)
|
||||
.collect(toImmutableList()))
|
||||
.containsNoneIn(badRefs);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> createNRefsOnCommit(
|
||||
Repository usersRepo, ObjectId commitId, int n, int uuid) throws IOException {
|
||||
List<String> refNames = new ArrayList<>();
|
||||
BatchRefUpdate bru = usersRepo.getRefDatabase().newBatchUpdate();
|
||||
bru.setAtomic(true);
|
||||
for (int i = 2; i <= n + 1; i++) {
|
||||
String refName = getRefName(i, uuid);
|
||||
bru.addCommand(
|
||||
new ReceiveCommand(ObjectId.zeroId(), commitId, refName, ReceiveCommand.Type.CREATE));
|
||||
refNames.add(refName);
|
||||
}
|
||||
RefUpdateUtil.executeChecked(bru, usersRepo);
|
||||
return refNames;
|
||||
}
|
||||
|
||||
private static String getRefName(int changeId, int userId) {
|
||||
Change.Id cId = Change.id(changeId);
|
||||
Account.Id aId = Account.id(userId);
|
||||
return RefNames.refsDraftComments(cId, aId);
|
||||
}
|
||||
|
||||
private static Ref createRefWithNonEmptyTreeCommit(Repository usersRepo, int changeId, int userId)
|
||||
throws IOException {
|
||||
RevWalk rw = new RevWalk(usersRepo);
|
||||
ObjectId fileObj = createBlob(usersRepo, String.format("file %d content", changeId));
|
||||
ObjectId treeObj =
|
||||
createTree(usersRepo, rw.lookupBlob(fileObj), String.format("file%d.txt", changeId));
|
||||
ObjectId commitObj = createCommit(usersRepo, treeObj, null);
|
||||
Ref refObj = createRef(usersRepo, commitObj, getRefName(changeId, userId));
|
||||
return refObj;
|
||||
}
|
||||
|
||||
private static Ref createRefWithEmptyTreeCommit(Repository usersRepo, int changeId, int userId)
|
||||
throws IOException {
|
||||
ObjectId treeEmpty = createTree(usersRepo, null, "");
|
||||
ObjectId commitObj = createCommit(usersRepo, treeEmpty, null);
|
||||
Ref refObj = createRef(usersRepo, commitObj, getRefName(changeId, userId));
|
||||
return refObj;
|
||||
}
|
||||
|
||||
private static Ref createRef(Repository repo, ObjectId commitId, String refName)
|
||||
throws IOException {
|
||||
RefUpdate update = repo.updateRef(refName);
|
||||
update.setNewObjectId(commitId);
|
||||
update.setForceUpdate(true);
|
||||
update.update();
|
||||
return repo.exactRef(refName);
|
||||
}
|
||||
|
||||
private static ObjectId createCommit(Repository repo, ObjectId treeId, ObjectId parentCommit)
|
||||
throws IOException {
|
||||
try (ObjectInserter oi = repo.newObjectInserter()) {
|
||||
PersonIdent committer =
|
||||
new PersonIdent(new PersonIdent("Foo Bar", "foo.bar@baz.com"), TimeUtil.nowTs());
|
||||
CommitBuilder cb = new CommitBuilder();
|
||||
cb.setTreeId(treeId);
|
||||
cb.setCommitter(committer);
|
||||
cb.setAuthor(committer);
|
||||
cb.setMessage("Test commit");
|
||||
if (parentCommit != null) {
|
||||
cb.setParentIds(parentCommit);
|
||||
}
|
||||
ObjectId commitId = oi.insert(cb);
|
||||
oi.flush();
|
||||
oi.close();
|
||||
return commitId;
|
||||
}
|
||||
}
|
||||
|
||||
private static ObjectId createTree(Repository repo, RevBlob blob, String blobName)
|
||||
throws IOException {
|
||||
try (ObjectInserter oi = repo.newObjectInserter()) {
|
||||
TreeFormatter formatter = new TreeFormatter();
|
||||
if (blob != null) {
|
||||
formatter.append(blobName, blob);
|
||||
}
|
||||
ObjectId treeId = oi.insert(formatter);
|
||||
oi.flush();
|
||||
oi.close();
|
||||
return treeId;
|
||||
}
|
||||
}
|
||||
|
||||
private static ObjectId createBlob(Repository repo, String content) throws IOException {
|
||||
try (ObjectInserter oi = repo.newObjectInserter()) {
|
||||
ObjectId blobId = oi.insert(Constants.OBJ_BLOB, content.getBytes(UTF_8));
|
||||
oi.flush();
|
||||
oi.close();
|
||||
return blobId;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
_template = """
|
||||
load("@bazel_skylib//lib:versions.bzl", "versions")
|
||||
|
||||
def check_bazel_version():
|
||||
versions.check(minimum_bazel_version = "{version}")
|
||||
""".strip()
|
||||
|
||||
def _impl(repository_ctx):
|
||||
repository_ctx.symlink(Label("@//:.bazelversion"), ".bazelversion")
|
||||
bazelversion = repository_ctx.read(".bazelversion").strip()
|
||||
|
||||
repository_ctx.file("BUILD", executable = False)
|
||||
|
||||
repository_ctx.file("check.bzl", executable = False, content = _template.format(version = bazelversion))
|
||||
|
||||
bazelisk_version = repository_rule(implementation = _impl)
|
Loading…
Reference in New Issue
Block a user