Merge branch 'stable-3.0' into stable-3.1
* stable-3.0: ChangeEditModifier: Reject invalid file paths as '400 Bad Request' Remove orphan library downloader facility Fix a bug where _moreChanges is set to the wrong value Don't check conflicting ref names when deleting refs Change-Id: Ic18373c4b4024de7bb9a50a8ac22651435e3b9cf
This commit is contained in:
@@ -150,6 +150,7 @@ public class RefUpdateUtil {
|
|||||||
public static void deleteChecked(Repository repo, String refName) throws IOException {
|
public static void deleteChecked(Repository repo, String refName) throws IOException {
|
||||||
RefUpdate ru = repo.updateRef(refName);
|
RefUpdate ru = repo.updateRef(refName);
|
||||||
ru.setForceUpdate(true);
|
ru.setForceUpdate(true);
|
||||||
|
ru.setCheckConflicting(false);
|
||||||
switch (ru.delete()) {
|
switch (ru.delete()) {
|
||||||
case FORCED:
|
case FORCED:
|
||||||
// Ref was deleted.
|
// Ref was deleted.
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ public class InitModule extends FactoryModule {
|
|||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(SitePaths.class);
|
bind(SitePaths.class);
|
||||||
bind(Libraries.class);
|
|
||||||
bind(LibraryDownloader.class);
|
|
||||||
factory(Section.Factory.class);
|
factory(Section.Factory.class);
|
||||||
factory(VersionedAuthorizedKeysOnInit.Factory.class);
|
factory(VersionedAuthorizedKeysOnInit.Factory.class);
|
||||||
|
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
// Copyright (C) 2009 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.init;
|
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
||||||
|
|
||||||
import com.google.gerrit.pgm.init.api.LibraryDownload;
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
import com.google.inject.Singleton;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.List;
|
|
||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
|
||||||
import org.eclipse.jgit.lib.Config;
|
|
||||||
|
|
||||||
/** Standard {@link LibraryDownloader} instances derived from configuration. */
|
|
||||||
@Singleton
|
|
||||||
class Libraries {
|
|
||||||
private static final String RESOURCE_FILE = "com/google/gerrit/pgm/init/libraries.config";
|
|
||||||
|
|
||||||
private final Provider<LibraryDownloader> downloadProvider;
|
|
||||||
private final List<String> skippedDownloads;
|
|
||||||
private final boolean skipAllDownloads;
|
|
||||||
|
|
||||||
/* final */ LibraryDownloader db2Driver;
|
|
||||||
/* final */ LibraryDownloader db2DriverLicense;
|
|
||||||
/* final */ LibraryDownloader hanaDriver;
|
|
||||||
/* final */ LibraryDownloader mariadbDriver;
|
|
||||||
/* final */ LibraryDownloader mysqlDriver;
|
|
||||||
/* final */ LibraryDownloader oracleDriver;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
Libraries(
|
|
||||||
final Provider<LibraryDownloader> downloadProvider,
|
|
||||||
@LibraryDownload List<String> skippedDownloads,
|
|
||||||
@LibraryDownload Boolean skipAllDownloads) {
|
|
||||||
this.downloadProvider = downloadProvider;
|
|
||||||
this.skippedDownloads = skippedDownloads;
|
|
||||||
this.skipAllDownloads = skipAllDownloads;
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
final Config cfg = new Config();
|
|
||||||
try {
|
|
||||||
cfg.fromText(read(RESOURCE_FILE));
|
|
||||||
} catch (IOException | ConfigInvalidException e) {
|
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Field f : Libraries.class.getDeclaredFields()) {
|
|
||||||
if ((f.getModifiers() & Modifier.STATIC) == 0 && f.getType() == LibraryDownloader.class) {
|
|
||||||
try {
|
|
||||||
f.set(this, downloadProvider.get());
|
|
||||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
|
||||||
throw new IllegalStateException("Cannot initialize " + f.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Field f : Libraries.class.getDeclaredFields()) {
|
|
||||||
if ((f.getModifiers() & Modifier.STATIC) == 0 && f.getType() == LibraryDownloader.class) {
|
|
||||||
try {
|
|
||||||
init(f, cfg);
|
|
||||||
} catch (IllegalArgumentException
|
|
||||||
| IllegalAccessException
|
|
||||||
| NoSuchFieldException
|
|
||||||
| SecurityException e) {
|
|
||||||
throw new IllegalStateException("Cannot configure " + f.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(Field field, Config cfg)
|
|
||||||
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
|
|
||||||
SecurityException {
|
|
||||||
String n = field.getName();
|
|
||||||
LibraryDownloader dl = (LibraryDownloader) field.get(this);
|
|
||||||
dl.setName(get(cfg, n, "name"));
|
|
||||||
dl.setJarUrl(get(cfg, n, "url"));
|
|
||||||
dl.setSHA1(getOptional(cfg, n, "sha1"));
|
|
||||||
dl.setRemove(get(cfg, n, "remove"));
|
|
||||||
for (String d : cfg.getStringList("library", n, "needs")) {
|
|
||||||
dl.addNeeds((LibraryDownloader) getClass().getDeclaredField(d).get(this));
|
|
||||||
}
|
|
||||||
dl.setSkipDownload(skipAllDownloads || skippedDownloads.contains(n));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getOptional(Config cfg, String name, String key) {
|
|
||||||
return doGet(cfg, name, key, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String get(Config cfg, String name, String key) {
|
|
||||||
return doGet(cfg, name, key, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String doGet(Config cfg, String name, String key, boolean required) {
|
|
||||||
String val = cfg.getString("library", name, key);
|
|
||||||
if ((val == null || val.isEmpty()) && required) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Variable library." + name + "." + key + " is required within " + RESOURCE_FILE);
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String read(String p) throws IOException {
|
|
||||||
try (InputStream in = Libraries.class.getClassLoader().getResourceAsStream(p)) {
|
|
||||||
if (in == null) {
|
|
||||||
throw new FileNotFoundException("Cannot load resource " + p);
|
|
||||||
}
|
|
||||||
try (Reader r = new InputStreamReader(in, UTF_8)) {
|
|
||||||
final StringBuilder buf = new StringBuilder();
|
|
||||||
final char[] tmp = new char[512];
|
|
||||||
int n;
|
|
||||||
while (0 < (n = r.read(tmp))) {
|
|
||||||
buf.append(tmp, 0, n);
|
|
||||||
}
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,316 +0,0 @@
|
|||||||
// Copyright (C) 2009 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.init;
|
|
||||||
|
|
||||||
import com.google.common.hash.Funnels;
|
|
||||||
import com.google.common.hash.Hasher;
|
|
||||||
import com.google.common.hash.Hashing;
|
|
||||||
import com.google.common.io.ByteStreams;
|
|
||||||
import com.google.gerrit.common.Die;
|
|
||||||
import com.google.gerrit.common.IoUtil;
|
|
||||||
import com.google.gerrit.pgm.init.api.ConsoleUI;
|
|
||||||
import com.google.gerrit.server.config.SitePaths;
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.Proxy;
|
|
||||||
import java.net.ProxySelector;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import org.eclipse.jgit.util.HttpSupport;
|
|
||||||
|
|
||||||
/** Get optional or required 3rd party library files into $site_path/lib. */
|
|
||||||
class LibraryDownloader {
|
|
||||||
private final ConsoleUI ui;
|
|
||||||
private final Path lib_dir;
|
|
||||||
private final StaleLibraryRemover remover;
|
|
||||||
|
|
||||||
private boolean required;
|
|
||||||
private String name;
|
|
||||||
private String jarUrl;
|
|
||||||
private String sha1;
|
|
||||||
private String remove;
|
|
||||||
private List<LibraryDownloader> needs;
|
|
||||||
private LibraryDownloader neededBy;
|
|
||||||
private Path dst;
|
|
||||||
private boolean download; // download or copy
|
|
||||||
private boolean exists;
|
|
||||||
private boolean skipDownload;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
LibraryDownloader(ConsoleUI ui, SitePaths site, StaleLibraryRemover remover) {
|
|
||||||
this.ui = ui;
|
|
||||||
this.lib_dir = site.lib_dir;
|
|
||||||
this.remover = remover;
|
|
||||||
this.needs = new ArrayList<>(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setJarUrl(String url) {
|
|
||||||
this.jarUrl = url;
|
|
||||||
download = jarUrl.startsWith("http");
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSHA1(String sha1) {
|
|
||||||
this.sha1 = sha1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setRemove(String remove) {
|
|
||||||
this.remove = remove;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addNeeds(LibraryDownloader lib) {
|
|
||||||
needs.add(lib);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSkipDownload(boolean skipDownload) {
|
|
||||||
this.skipDownload = skipDownload;
|
|
||||||
}
|
|
||||||
|
|
||||||
void downloadRequired() {
|
|
||||||
setRequired(true);
|
|
||||||
download();
|
|
||||||
}
|
|
||||||
|
|
||||||
void downloadOptional() {
|
|
||||||
required = false;
|
|
||||||
download();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setRequired(boolean r) {
|
|
||||||
required = r;
|
|
||||||
for (LibraryDownloader d : needs) {
|
|
||||||
d.setRequired(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void download() {
|
|
||||||
if (skipDownload) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jarUrl == null || !jarUrl.contains("/")) {
|
|
||||||
throw new IllegalStateException("Invalid JarUrl for " + name);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String jarName = jarUrl.substring(jarUrl.lastIndexOf('/') + 1);
|
|
||||||
if (jarName.contains("/") || jarName.contains("\\")) {
|
|
||||||
throw new IllegalStateException("Invalid JarUrl: " + jarUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name == null) {
|
|
||||||
name = jarName;
|
|
||||||
}
|
|
||||||
|
|
||||||
dst = lib_dir.resolve(jarName);
|
|
||||||
if (Files.exists(dst)) {
|
|
||||||
exists = true;
|
|
||||||
} else if (shouldGet()) {
|
|
||||||
doGet();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exists) {
|
|
||||||
for (LibraryDownloader d : needs) {
|
|
||||||
d.neededBy = this;
|
|
||||||
d.downloadRequired();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldGet() {
|
|
||||||
if (ui.isBatch()) {
|
|
||||||
return required;
|
|
||||||
}
|
|
||||||
final StringBuilder msg = new StringBuilder();
|
|
||||||
msg.append("\n");
|
|
||||||
msg.append("Gerrit Code Review is not shipped with %s\n");
|
|
||||||
if (neededBy != null) {
|
|
||||||
msg.append(String.format("** This library is required by %s. **\n", neededBy.name));
|
|
||||||
} else if (required) {
|
|
||||||
msg.append("** This library is required for your configuration. **\n");
|
|
||||||
} else {
|
|
||||||
msg.append(" If available, Gerrit can take advantage of features\n");
|
|
||||||
msg.append(" in the library, but will also function without it.\n");
|
|
||||||
}
|
|
||||||
msg.append(String.format("%s and install it now", download ? "Download" : "Copy"));
|
|
||||||
return ui.yesno(true, msg.toString(), name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doGet() {
|
|
||||||
if (!Files.exists(lib_dir)) {
|
|
||||||
try {
|
|
||||||
Files.createDirectories(lib_dir);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new Die("Cannot create " + lib_dir, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
remover.remove(remove);
|
|
||||||
if (download) {
|
|
||||||
doGetByHttp();
|
|
||||||
} else {
|
|
||||||
doGetByLocalCopy();
|
|
||||||
}
|
|
||||||
verifyFileChecksum();
|
|
||||||
} catch (IOException err) {
|
|
||||||
try {
|
|
||||||
Files.delete(dst);
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Delete failed; leave alone.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ui.isBatch()) {
|
|
||||||
throw new Die("error: Cannot get " + jarUrl, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
System.err.println();
|
|
||||||
System.err.println();
|
|
||||||
System.err.println("error: " + err.getMessage());
|
|
||||||
System.err.println("Please download:");
|
|
||||||
System.err.println();
|
|
||||||
System.err.println(" " + jarUrl);
|
|
||||||
System.err.println();
|
|
||||||
System.err.println("and save as:");
|
|
||||||
System.err.println();
|
|
||||||
System.err.println(" " + dst.toAbsolutePath());
|
|
||||||
System.err.println();
|
|
||||||
System.err.flush();
|
|
||||||
|
|
||||||
ui.waitForUser();
|
|
||||||
|
|
||||||
if (Files.exists(dst)) {
|
|
||||||
verifyFileChecksum();
|
|
||||||
|
|
||||||
} else if (!ui.yesno(!required, "Continue without this library")) {
|
|
||||||
throw new Die("aborted by user");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Files.exists(dst)) {
|
|
||||||
exists = true;
|
|
||||||
IoUtil.loadJARs(dst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doGetByLocalCopy() throws IOException {
|
|
||||||
System.err.print("Copying " + jarUrl + " ...");
|
|
||||||
Path p = url2file(jarUrl);
|
|
||||||
if (!Files.exists(p)) {
|
|
||||||
StringBuilder msg =
|
|
||||||
new StringBuilder()
|
|
||||||
.append("\n")
|
|
||||||
.append("Can not find the %s at this location: %s\n")
|
|
||||||
.append("Please provide alternative URL");
|
|
||||||
p = url2file(ui.readString(null, msg.toString(), name, jarUrl));
|
|
||||||
}
|
|
||||||
Files.copy(p, dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Path url2file(String urlString) throws IOException {
|
|
||||||
final URL url = new URL(urlString);
|
|
||||||
try {
|
|
||||||
return Paths.get(url.toURI());
|
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
return Paths.get(url.getPath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doGetByHttp() throws IOException {
|
|
||||||
System.err.print("Downloading " + jarUrl + " ...");
|
|
||||||
System.err.flush();
|
|
||||||
try (InputStream in = openHttpStream(jarUrl);
|
|
||||||
OutputStream out = Files.newOutputStream(dst)) {
|
|
||||||
ByteStreams.copy(in, out);
|
|
||||||
System.err.println(" OK");
|
|
||||||
System.err.flush();
|
|
||||||
} catch (IOException err) {
|
|
||||||
deleteDst();
|
|
||||||
System.err.println(" !! FAIL !!");
|
|
||||||
System.err.println(err);
|
|
||||||
System.err.flush();
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static InputStream openHttpStream(String urlStr) throws IOException {
|
|
||||||
ProxySelector proxySelector = ProxySelector.getDefault();
|
|
||||||
URL url = new URL(urlStr);
|
|
||||||
Proxy proxy = HttpSupport.proxyFor(proxySelector, url);
|
|
||||||
HttpURLConnection c = (HttpURLConnection) url.openConnection(proxy);
|
|
||||||
|
|
||||||
switch (HttpSupport.response(c)) {
|
|
||||||
case HttpURLConnection.HTTP_OK:
|
|
||||||
return c.getInputStream();
|
|
||||||
|
|
||||||
case HttpURLConnection.HTTP_NOT_FOUND:
|
|
||||||
throw new FileNotFoundException(url.toString());
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new IOException(
|
|
||||||
url.toString() + ": " + HttpSupport.response(c) + " " + c.getResponseMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Use Hashing.sha1 for compatibility.
|
|
||||||
private void verifyFileChecksum() {
|
|
||||||
if (sha1 == null) {
|
|
||||||
System.err.println();
|
|
||||||
System.err.flush();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Hasher h = Hashing.sha1().newHasher();
|
|
||||||
try (InputStream in = Files.newInputStream(dst);
|
|
||||||
OutputStream out = Funnels.asOutputStream(h)) {
|
|
||||||
ByteStreams.copy(in, out);
|
|
||||||
} catch (IOException e) {
|
|
||||||
deleteDst();
|
|
||||||
throw new Die("cannot checksum " + dst, e);
|
|
||||||
}
|
|
||||||
if (sha1.equals(h.hash().toString())) {
|
|
||||||
System.err.println("Checksum " + dst.getFileName() + " OK");
|
|
||||||
System.err.flush();
|
|
||||||
} else if (ui.isBatch()) {
|
|
||||||
deleteDst();
|
|
||||||
throw new Die(dst + " SHA-1 checksum does not match");
|
|
||||||
|
|
||||||
} else if (!ui.yesno(
|
|
||||||
null /* force an answer */,
|
|
||||||
"error: SHA-1 checksum does not match\nUse %s anyway", //
|
|
||||||
dst.getFileName())) {
|
|
||||||
deleteDst();
|
|
||||||
throw new Die("aborted by user");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteDst() {
|
|
||||||
try {
|
|
||||||
Files.delete(dst);
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println(" Failed to clean up lib: " + dst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -51,6 +51,7 @@ import java.sql.Timestamp;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
import org.eclipse.jgit.dircache.InvalidPathException;
|
||||||
import org.eclipse.jgit.lib.BatchRefUpdate;
|
import org.eclipse.jgit.lib.BatchRefUpdate;
|
||||||
import org.eclipse.jgit.lib.CommitBuilder;
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
import org.eclipse.jgit.lib.NullProgressMonitor;
|
import org.eclipse.jgit.lib.NullProgressMonitor;
|
||||||
@@ -243,13 +244,14 @@ public class ChangeEditModifier {
|
|||||||
* @param filePath the path of the file whose contents should be modified
|
* @param filePath the path of the file whose contents should be modified
|
||||||
* @param newContent the new file content
|
* @param newContent the new file content
|
||||||
* @throws AuthException if the user isn't authenticated or not allowed to use change edits
|
* @throws AuthException if the user isn't authenticated or not allowed to use change edits
|
||||||
|
* @throws BadRequestException if the user provided bad input (e.g. invalid file paths)
|
||||||
* @throws InvalidChangeOperationException if the file already had the specified content
|
* @throws InvalidChangeOperationException if the file already had the specified content
|
||||||
* @throws PermissionBackendException
|
* @throws PermissionBackendException
|
||||||
* @throws ResourceConflictException if the project state does not permit the operation
|
* @throws ResourceConflictException if the project state does not permit the operation
|
||||||
*/
|
*/
|
||||||
public void modifyFile(
|
public void modifyFile(
|
||||||
Repository repository, ChangeNotes notes, String filePath, RawInput newContent)
|
Repository repository, ChangeNotes notes, String filePath, RawInput newContent)
|
||||||
throws AuthException, InvalidChangeOperationException, IOException,
|
throws AuthException, BadRequestException, InvalidChangeOperationException, IOException,
|
||||||
PermissionBackendException, ResourceConflictException {
|
PermissionBackendException, ResourceConflictException {
|
||||||
modifyTree(repository, notes, new ChangeFileContentModification(filePath, newContent));
|
modifyTree(repository, notes, new ChangeFileContentModification(filePath, newContent));
|
||||||
}
|
}
|
||||||
@@ -262,12 +264,13 @@ public class ChangeEditModifier {
|
|||||||
* @param notes the {@link ChangeNotes} of the change whose change edit should be modified
|
* @param notes the {@link ChangeNotes} of the change whose change edit should be modified
|
||||||
* @param file path of the file which should be deleted
|
* @param file path of the file which should be deleted
|
||||||
* @throws AuthException if the user isn't authenticated or not allowed to use change edits
|
* @throws AuthException if the user isn't authenticated or not allowed to use change edits
|
||||||
|
* @throws BadRequestException if the user provided bad input (e.g. invalid file paths)
|
||||||
* @throws InvalidChangeOperationException if the file does not exist
|
* @throws InvalidChangeOperationException if the file does not exist
|
||||||
* @throws PermissionBackendException
|
* @throws PermissionBackendException
|
||||||
* @throws ResourceConflictException if the project state does not permit the operation
|
* @throws ResourceConflictException if the project state does not permit the operation
|
||||||
*/
|
*/
|
||||||
public void deleteFile(Repository repository, ChangeNotes notes, String file)
|
public void deleteFile(Repository repository, ChangeNotes notes, String file)
|
||||||
throws AuthException, InvalidChangeOperationException, IOException,
|
throws AuthException, BadRequestException, InvalidChangeOperationException, IOException,
|
||||||
PermissionBackendException, ResourceConflictException {
|
PermissionBackendException, ResourceConflictException {
|
||||||
modifyTree(repository, notes, new DeleteFileModification(file));
|
modifyTree(repository, notes, new DeleteFileModification(file));
|
||||||
}
|
}
|
||||||
@@ -281,6 +284,7 @@ public class ChangeEditModifier {
|
|||||||
* @param currentFilePath the current path/name of the file
|
* @param currentFilePath the current path/name of the file
|
||||||
* @param newFilePath the desired path/name of the file
|
* @param newFilePath the desired path/name of the file
|
||||||
* @throws AuthException if the user isn't authenticated or not allowed to use change edits
|
* @throws AuthException if the user isn't authenticated or not allowed to use change edits
|
||||||
|
* @throws BadRequestException if the user provided bad input (e.g. invalid file paths)
|
||||||
* @throws InvalidChangeOperationException if the file was already renamed to the specified new
|
* @throws InvalidChangeOperationException if the file was already renamed to the specified new
|
||||||
* name
|
* name
|
||||||
* @throws PermissionBackendException
|
* @throws PermissionBackendException
|
||||||
@@ -288,7 +292,7 @@ public class ChangeEditModifier {
|
|||||||
*/
|
*/
|
||||||
public void renameFile(
|
public void renameFile(
|
||||||
Repository repository, ChangeNotes notes, String currentFilePath, String newFilePath)
|
Repository repository, ChangeNotes notes, String currentFilePath, String newFilePath)
|
||||||
throws AuthException, InvalidChangeOperationException, IOException,
|
throws AuthException, BadRequestException, InvalidChangeOperationException, IOException,
|
||||||
PermissionBackendException, ResourceConflictException {
|
PermissionBackendException, ResourceConflictException {
|
||||||
modifyTree(repository, notes, new RenameFileModification(currentFilePath, newFilePath));
|
modifyTree(repository, notes, new RenameFileModification(currentFilePath, newFilePath));
|
||||||
}
|
}
|
||||||
@@ -306,14 +310,14 @@ public class ChangeEditModifier {
|
|||||||
* @throws PermissionBackendException
|
* @throws PermissionBackendException
|
||||||
*/
|
*/
|
||||||
public void restoreFile(Repository repository, ChangeNotes notes, String file)
|
public void restoreFile(Repository repository, ChangeNotes notes, String file)
|
||||||
throws AuthException, InvalidChangeOperationException, IOException,
|
throws AuthException, BadRequestException, InvalidChangeOperationException, IOException,
|
||||||
PermissionBackendException, ResourceConflictException {
|
PermissionBackendException, ResourceConflictException {
|
||||||
modifyTree(repository, notes, new RestoreFileModification(file));
|
modifyTree(repository, notes, new RestoreFileModification(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void modifyTree(
|
private void modifyTree(
|
||||||
Repository repository, ChangeNotes notes, TreeModification treeModification)
|
Repository repository, ChangeNotes notes, TreeModification treeModification)
|
||||||
throws AuthException, IOException, InvalidChangeOperationException,
|
throws AuthException, BadRequestException, IOException, InvalidChangeOperationException,
|
||||||
PermissionBackendException, ResourceConflictException {
|
PermissionBackendException, ResourceConflictException {
|
||||||
assertCanEdit(notes);
|
assertCanEdit(notes);
|
||||||
|
|
||||||
@@ -358,8 +362,8 @@ public class ChangeEditModifier {
|
|||||||
ChangeNotes notes,
|
ChangeNotes notes,
|
||||||
PatchSet patchSet,
|
PatchSet patchSet,
|
||||||
List<TreeModification> treeModifications)
|
List<TreeModification> treeModifications)
|
||||||
throws AuthException, IOException, InvalidChangeOperationException, MergeConflictException,
|
throws AuthException, BadRequestException, IOException, InvalidChangeOperationException,
|
||||||
PermissionBackendException, ResourceConflictException {
|
MergeConflictException, PermissionBackendException, ResourceConflictException {
|
||||||
assertCanEdit(notes);
|
assertCanEdit(notes);
|
||||||
|
|
||||||
Optional<ChangeEdit> optionalChangeEdit = lookupChangeEdit(notes);
|
Optional<ChangeEdit> optionalChangeEdit = lookupChangeEdit(notes);
|
||||||
@@ -469,10 +473,15 @@ public class ChangeEditModifier {
|
|||||||
|
|
||||||
private static ObjectId createNewTree(
|
private static ObjectId createNewTree(
|
||||||
Repository repository, RevCommit baseCommit, List<TreeModification> treeModifications)
|
Repository repository, RevCommit baseCommit, List<TreeModification> treeModifications)
|
||||||
throws IOException, InvalidChangeOperationException {
|
throws BadRequestException, IOException, InvalidChangeOperationException {
|
||||||
TreeCreator treeCreator = new TreeCreator(baseCommit);
|
ObjectId newTreeId;
|
||||||
treeCreator.addTreeModifications(treeModifications);
|
try {
|
||||||
ObjectId newTreeId = treeCreator.createNewTreeAndGetId(repository);
|
TreeCreator treeCreator = new TreeCreator(baseCommit);
|
||||||
|
treeCreator.addTreeModifications(treeModifications);
|
||||||
|
newTreeId = treeCreator.createNewTreeAndGetId(repository);
|
||||||
|
} catch (InvalidPathException e) {
|
||||||
|
throw new BadRequestException(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
if (ObjectId.isEqual(newTreeId, baseCommit.getTree())) {
|
if (ObjectId.isEqual(newTreeId, baseCommit.getTree())) {
|
||||||
throw new InvalidChangeOperationException("no changes were made");
|
throw new InvalidChangeOperationException("no changes were made");
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import com.google.gerrit.entities.PatchSet;
|
|||||||
import com.google.gerrit.entities.Project;
|
import com.google.gerrit.entities.Project;
|
||||||
import com.google.gerrit.extensions.common.EditInfo;
|
import com.google.gerrit.extensions.common.EditInfo;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
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.ResourceConflictException;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
import com.google.gerrit.extensions.restapi.Response;
|
import com.google.gerrit.extensions.restapi.Response;
|
||||||
@@ -65,8 +66,8 @@ public class ApplyFix implements RestModifyView<FixResource, Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<EditInfo> apply(FixResource fixResource, Void nothing)
|
public Response<EditInfo> apply(FixResource fixResource, Void nothing)
|
||||||
throws AuthException, ResourceConflictException, IOException, ResourceNotFoundException,
|
throws AuthException, BadRequestException, ResourceConflictException, IOException,
|
||||||
PermissionBackendException {
|
ResourceNotFoundException, PermissionBackendException {
|
||||||
RevisionResource revisionResource = fixResource.getRevisionResource();
|
RevisionResource revisionResource = fixResource.getRevisionResource();
|
||||||
Project.NameKey project = revisionResource.getProject();
|
Project.NameKey project = revisionResource.getProject();
|
||||||
ProjectState projectState = projectCache.checkedGet(project);
|
ProjectState projectState = projectCache.checkedGet(project);
|
||||||
|
|||||||
@@ -118,7 +118,8 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<?> apply(ChangeResource resource, IdString id, Put.Input input)
|
public Response<?> apply(ChangeResource resource, IdString id, Put.Input input)
|
||||||
throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
|
throws AuthException, ResourceConflictException, BadRequestException, IOException,
|
||||||
|
PermissionBackendException {
|
||||||
putEdit.apply(resource, id.get(), input.content);
|
putEdit.apply(resource, id.get(), input.content);
|
||||||
return Response.none();
|
return Response.none();
|
||||||
}
|
}
|
||||||
@@ -135,7 +136,8 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<?> apply(ChangeResource rsrc, IdString id, Input in)
|
public Response<?> apply(ChangeResource rsrc, IdString id, Input in)
|
||||||
throws IOException, AuthException, ResourceConflictException, PermissionBackendException {
|
throws IOException, AuthException, BadRequestException, ResourceConflictException,
|
||||||
|
PermissionBackendException {
|
||||||
return deleteContent.apply(rsrc, id.get());
|
return deleteContent.apply(rsrc, id.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,7 +238,8 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<?> apply(ChangeResource resource, Post.Input input)
|
public Response<?> apply(ChangeResource resource, Post.Input input)
|
||||||
throws AuthException, IOException, ResourceConflictException, PermissionBackendException {
|
throws AuthException, BadRequestException, IOException, ResourceConflictException,
|
||||||
|
PermissionBackendException {
|
||||||
Project.NameKey project = resource.getProject();
|
Project.NameKey project = resource.getProject();
|
||||||
try (Repository repository = repositoryManager.openRepository(project)) {
|
try (Repository repository = repositoryManager.openRepository(project)) {
|
||||||
if (isRestoreFile(input)) {
|
if (isRestoreFile(input)) {
|
||||||
@@ -281,12 +284,14 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<?> apply(ChangeEditResource rsrc, Input input)
|
public Response<?> apply(ChangeEditResource rsrc, Input input)
|
||||||
throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
|
throws AuthException, ResourceConflictException, BadRequestException, IOException,
|
||||||
|
PermissionBackendException {
|
||||||
return apply(rsrc.getChangeResource(), rsrc.getPath(), input.content);
|
return apply(rsrc.getChangeResource(), rsrc.getPath(), input.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response<?> apply(ChangeResource rsrc, String path, RawInput newContent)
|
public Response<?> apply(ChangeResource rsrc, String path, RawInput newContent)
|
||||||
throws ResourceConflictException, AuthException, IOException, PermissionBackendException {
|
throws ResourceConflictException, AuthException, BadRequestException, IOException,
|
||||||
|
PermissionBackendException {
|
||||||
if (Strings.isNullOrEmpty(path) || path.charAt(0) == '/') {
|
if (Strings.isNullOrEmpty(path) || path.charAt(0) == '/') {
|
||||||
throw new ResourceConflictException("Invalid path: " + path);
|
throw new ResourceConflictException("Invalid path: " + path);
|
||||||
}
|
}
|
||||||
@@ -320,12 +325,14 @@ public class ChangeEdits implements ChildCollection<ChangeResource, ChangeEditRe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<?> apply(ChangeEditResource rsrc, Input input)
|
public Response<?> apply(ChangeEditResource rsrc, Input input)
|
||||||
throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
|
throws AuthException, BadRequestException, ResourceConflictException, IOException,
|
||||||
|
PermissionBackendException {
|
||||||
return apply(rsrc.getChangeResource(), rsrc.getPath());
|
return apply(rsrc.getChangeResource(), rsrc.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response<?> apply(ChangeResource rsrc, String filePath)
|
public Response<?> apply(ChangeResource rsrc, String filePath)
|
||||||
throws AuthException, IOException, ResourceConflictException, PermissionBackendException {
|
throws AuthException, BadRequestException, IOException, ResourceConflictException,
|
||||||
|
PermissionBackendException {
|
||||||
try (Repository repository = repositoryManager.openRepository(rsrc.getProject())) {
|
try (Repository repository = repositoryManager.openRepository(rsrc.getProject())) {
|
||||||
editModifier.deleteFile(repository, rsrc.getNotes(), filePath);
|
editModifier.deleteFile(repository, rsrc.getNotes(), filePath);
|
||||||
} catch (InvalidChangeOperationException e) {
|
} catch (InvalidChangeOperationException e) {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ import com.google.gerrit.extensions.common.DiffInfo;
|
|||||||
import com.google.gerrit.extensions.common.EditInfo;
|
import com.google.gerrit.extensions.common.EditInfo;
|
||||||
import com.google.gerrit.extensions.common.FileInfo;
|
import com.google.gerrit.extensions.common.FileInfo;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||||
import com.google.gerrit.server.ChangeMessagesUtil;
|
import com.google.gerrit.server.ChangeMessagesUtil;
|
||||||
@@ -435,6 +436,16 @@ public class ChangeEditIT extends AbstractDaemonTest {
|
|||||||
assertThat(getFileContentOfEdit(changeId, FILE_NAME)).isAbsent();
|
assertThat(getFileContentOfEdit(changeId, FILE_NAME)).isAbsent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void renameExistingFileToInvalidPath() throws Exception {
|
||||||
|
createEmptyEditFor(changeId);
|
||||||
|
BadRequestException badRequest =
|
||||||
|
assertThrows(
|
||||||
|
BadRequestException.class,
|
||||||
|
() -> gApi.changes().id(changeId).edit().renameFile(FILE_NAME, "invalid/path/"));
|
||||||
|
assertThat(badRequest.getMessage()).isEqualTo("Invalid path: invalid/path/");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createEditByDeletingExistingFileRest() throws Exception {
|
public void createEditByDeletingExistingFileRest() throws Exception {
|
||||||
adminRestSession.delete(urlEditFile(changeId, FILE_NAME)).assertNoContent();
|
adminRestSession.delete(urlEditFile(changeId, FILE_NAME)).assertNoContent();
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
// Copyright (C) 2009 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.init;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
|
||||||
|
|
||||||
import com.google.gerrit.pgm.init.api.ConsoleUI;
|
|
||||||
import com.google.gerrit.server.config.SitePaths;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Collections;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
|
||||||
public class LibrariesTest {
|
|
||||||
@Mock ConsoleUI ui;
|
|
||||||
@Mock StaleLibraryRemover remover;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void create() throws Exception {
|
|
||||||
final SitePaths site = new SitePaths(Paths.get("."));
|
|
||||||
|
|
||||||
Libraries lib =
|
|
||||||
new Libraries(
|
|
||||||
() -> new LibraryDownloader(ui, site, remover), Collections.emptyList(), false);
|
|
||||||
|
|
||||||
assertNotNull(lib.mysqlDriver);
|
|
||||||
verifyZeroInteractions(ui);
|
|
||||||
verifyZeroInteractions(remover);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
# Copyright (C) 2009 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.
|
|
||||||
|
|
||||||
[library "mysqlDriver"]
|
|
||||||
name = MySQL Connector/J 5.1.43
|
|
||||||
url = https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.43/mysql-connector-java-5.1.43.jar
|
|
||||||
sha1 = dee9103eec0d877f3a21c82d4d9e9f4fbd2d6e0a
|
|
||||||
remove = mysql-connector-java-.*[.]jar
|
|
||||||
|
|
||||||
[library "mariadbDriver"]
|
|
||||||
name = MariaDB Connector/J 2.3.0
|
|
||||||
url = https://repo1.maven.org/maven2/org/mariadb/jdbc/mariadb-java-client/2.3.0/mariadb-java-client-2.3.0.jar
|
|
||||||
sha1 = c2b1a6002a169757d0649449288e9b3b776af76b
|
|
||||||
remove = mariadb-java-client-.*[.]jar
|
|
||||||
|
|
||||||
[library "oracleDriver"]
|
|
||||||
name = Oracle JDBC driver 11g Release 2 (11.2.0)
|
|
||||||
url = file:///u01/app/oracle/product/11.2.0/xe/jdbc/lib/ojdbc6.jar
|
|
||||||
sha1 = 2f89cd9176772c3a6c261ce6a8e3d0d4425f5679
|
|
||||||
remove = ojdbc6.jar
|
|
||||||
|
|
||||||
[library "db2Driver"]
|
|
||||||
name = DB2 Type 4 JDBC driver (10.5)
|
|
||||||
url = file:///opt/ibm/db2/V10.5/java/db2jcc4.jar
|
|
||||||
sha1 = 9344d4fd41d6511f2d1d1deb7759056495b3a39b
|
|
||||||
needs = db2DriverLicense
|
|
||||||
remove = db2jcc4.jar
|
|
||||||
|
|
||||||
# Omit SHA-1 for license JAR as it's not stable and depends on the product
|
|
||||||
# the customer has purchased.
|
|
||||||
[library "db2DriverLicense"]
|
|
||||||
name = DB2 Type 4 JDBC driver license (10.5)
|
|
||||||
url = file:///opt/ibm/db2/V10.5/java/db2jcc_license_cu.jar
|
|
||||||
remove = db2jcc_license_cu.jar
|
|
||||||
|
|
||||||
[library "hanaDriver"]
|
|
||||||
name = HANA JDBC driver
|
|
||||||
url = file:///usr/sap/hdbclient/ngdbc.jar
|
|
||||||
remove = ngdbc.jar
|
|
||||||
Reference in New Issue
Block a user