mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 00:15:44 +01:00
display error on startup, if previous version is older than 1.60
This commit is contained in:
@@ -63,6 +63,7 @@ import sonia.scm.util.IOUtil;
|
|||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
import javax.servlet.ServletContextEvent;
|
import javax.servlet.ServletContextEvent;
|
||||||
import javax.servlet.ServletContextListener;
|
import javax.servlet.ServletContextListener;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.xml.bind.DataBindingException;
|
import javax.xml.bind.DataBindingException;
|
||||||
import javax.xml.bind.JAXB;
|
import javax.xml.bind.JAXB;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
@@ -141,8 +142,11 @@ public class BootstrapContextListener implements ServletContextListener {
|
|||||||
Throwable startupError = SCMContext.getContext().getStartupError();
|
Throwable startupError = SCMContext.getContext().getStartupError();
|
||||||
if (startupError != null) {
|
if (startupError != null) {
|
||||||
contextListener = SingleView.error(startupError);
|
contextListener = SingleView.error(startupError);
|
||||||
|
} else if (Versions.isToOld()) {
|
||||||
|
contextListener = SingleView.view("/templates/to-old.mustache", HttpServletResponse.SC_CONFLICT);
|
||||||
} else {
|
} else {
|
||||||
createMigrationOrNormalContextListener();
|
createMigrationOrNormalContextListener();
|
||||||
|
Versions.writeNew();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,16 @@ final class SingleView {
|
|||||||
return new SingleViewContextListener(controller);
|
return new SingleViewContextListener(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ServletContextListener view(String template, int sc) {
|
||||||
|
ViewController controller = new SimpleViewController(template, request -> {
|
||||||
|
Object model = ImmutableMap.of(
|
||||||
|
"contextPath", request.getContextPath()
|
||||||
|
);
|
||||||
|
return new View(sc, model);
|
||||||
|
});
|
||||||
|
return new SingleViewContextListener(controller);
|
||||||
|
}
|
||||||
|
|
||||||
private static class SingleViewContextListener extends GuiceServletContextListener {
|
private static class SingleViewContextListener extends GuiceServletContextListener {
|
||||||
|
|
||||||
private final ViewController controller;
|
private final ViewController controller;
|
||||||
|
|||||||
77
scm-webapp/src/main/java/sonia/scm/boot/Versions.java
Normal file
77
scm-webapp/src/main/java/sonia/scm/boot/Versions.java
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package sonia.scm.boot;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sonia.scm.SCMContext;
|
||||||
|
import sonia.scm.SCMContextProvider;
|
||||||
|
import sonia.scm.util.IOUtil;
|
||||||
|
import sonia.scm.version.Version;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
class Versions {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(Versions.class);
|
||||||
|
|
||||||
|
private static final Version MIN_VERSION = Version.parse("1.60");
|
||||||
|
|
||||||
|
private final SCMContextProvider contextProvider;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
Versions(SCMContextProvider contextProvider) {
|
||||||
|
this.contextProvider = contextProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean isPreviousVersionToOld() {
|
||||||
|
return readVersion().map(v -> v.isOlder(MIN_VERSION)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void writeNewVersion() {
|
||||||
|
Path config = contextProvider.resolve(Paths.get("config"));
|
||||||
|
IOUtil.mkdirs(config.toFile());
|
||||||
|
|
||||||
|
String version = contextProvider.getVersion();
|
||||||
|
LOG.debug("write new version {} to file", version);
|
||||||
|
Path versionFile = config.resolve("version.txt");
|
||||||
|
try {
|
||||||
|
Files.write(versionFile, version.getBytes());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("failed to write version file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Version> readVersion() {
|
||||||
|
Path versionFile = contextProvider.resolve(Paths.get("config", "version.txt"));
|
||||||
|
if (versionFile.toFile().exists()) {
|
||||||
|
return Optional.of(readVersionFromFile(versionFile));
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Version readVersionFromFile(Path versionFile) {
|
||||||
|
try {
|
||||||
|
String versionString = new String(Files.readAllBytes(versionFile), StandardCharsets.UTF_8).trim();
|
||||||
|
LOG.debug("read previous version {} from file", versionString);
|
||||||
|
return Version.parse(versionString);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("failed to read version file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isToOld() {
|
||||||
|
return new Versions(SCMContext.getContext()).isPreviousVersionToOld();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeNew() {
|
||||||
|
new Versions(SCMContext.getContext()).writeNewVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
{{$title}}SCM-Manager Error{{/title}}
|
{{$title}}SCM-Manager Error{{/title}}
|
||||||
|
|
||||||
{{$content}}
|
{{$content}}
|
||||||
<h2>There is an error occurred during SCM-Manager startup.</h2>
|
<h2 class="subtitle">An error occurred during SCM-Manager startup.</h2>
|
||||||
|
|
||||||
<div class="notification is-danger">
|
<div class="notification is-danger">
|
||||||
<pre>
|
<pre>
|
||||||
|
|||||||
14
scm-webapp/src/main/resources/templates/to-old.mustache
Normal file
14
scm-webapp/src/main/resources/templates/to-old.mustache
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{{< layout}}
|
||||||
|
|
||||||
|
{{$title}}SCM-Manager Error{{/title}}
|
||||||
|
|
||||||
|
{{$content}}
|
||||||
|
<h2 class="subtitle">An error occurred during SCM-Manager startup.</h2>
|
||||||
|
|
||||||
|
<p class="notification is-danger">
|
||||||
|
We cannot migrate your SCM-Manager 1 installation,
|
||||||
|
because the version is too old.<br />
|
||||||
|
Please migrate to version 1.60 or newer, before migration to 2.x.
|
||||||
|
</p>
|
||||||
|
{{/content}}
|
||||||
|
{{/ layout}}
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
<!--
|
|
||||||
|
|
||||||
Copyright (c) 2010, Sebastian Sdorra
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
3. Neither the name of SCM-Manager; nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from this
|
|
||||||
software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
http://bitbucket.org/sdorra/scm-manager
|
|
||||||
|
|
||||||
|
|
||||||
-->
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>SCM-Manager Error</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
||||||
<style type="text/css">
|
|
||||||
body {
|
|
||||||
background-color: #ffffff;
|
|
||||||
margin: 10px;
|
|
||||||
color: #202020;
|
|
||||||
font-family: Verdana,Helvetica,Arial,sans-serif;
|
|
||||||
font-size: 75%;
|
|
||||||
}
|
|
||||||
h1, h2, h3, h4, h5 {
|
|
||||||
font-family: Arial, "Arial CE", "Lucida Grande CE", lucida, "Helvetica CE", sans-serif;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
color: #D20005;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 18px;
|
|
||||||
border-bottom: 1px solid #AFAFAF;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: 14px;
|
|
||||||
border-bottom: 1px solid #AFAFAF;
|
|
||||||
}
|
|
||||||
a:link, a:visited {
|
|
||||||
color: #045491;
|
|
||||||
font-weight: bold;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
a:link:hover, a:visited:hover {
|
|
||||||
color: #045491;
|
|
||||||
font-weight: bold;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border: 0 none;
|
|
||||||
border-collapse: collapse;
|
|
||||||
font-size: 100%;
|
|
||||||
margin: 20px 0;
|
|
||||||
padding: 20px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
td, th {
|
|
||||||
padding: 3px;
|
|
||||||
vertical-align: top;
|
|
||||||
border: 1px solid #CCCCCC;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
.small {
|
|
||||||
width: 20%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>SCM-Manager Error</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
There is an error occurred during SCM-Manager startup.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
{{error}}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -50,6 +50,18 @@ class SingleViewTest {
|
|||||||
guiceFilter.destroy();
|
guiceFilter.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCreateViewControllerForView() {
|
||||||
|
ServletContextListener listener = SingleView.view("/my-template", 409);
|
||||||
|
when(request.getContextPath()).thenReturn("/scm");
|
||||||
|
|
||||||
|
ViewController instance = findViewController(listener);
|
||||||
|
assertThat(instance.getTemplate()).isEqualTo("/my-template");
|
||||||
|
|
||||||
|
View view = instance.createView(request);
|
||||||
|
assertThat(view.getStatusCode()).isEqualTo(409);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldCreateViewControllerForError() {
|
void shouldCreateViewControllerForError() {
|
||||||
ServletContextListener listener = SingleView.error(new IOException("awesome io"));
|
ServletContextListener listener = SingleView.error(new IOException("awesome io"));
|
||||||
|
|||||||
86
scm-webapp/src/test/java/sonia/scm/boot/VersionsTest.java
Normal file
86
scm-webapp/src/test/java/sonia/scm/boot/VersionsTest.java
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package sonia.scm.boot;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junitpioneer.jupiter.TempDirectory;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.SCMContextProvider;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
|
||||||
|
@ExtendWith({MockitoExtension.class, TempDirectory.class})
|
||||||
|
class VersionsTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private SCMContextProvider contextProvider;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private Versions versions;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnTrueForVersionsPreviousTo160(@TempDirectory.TempDir Path directory) throws IOException {
|
||||||
|
setVersion(directory, "1.59");
|
||||||
|
assertThat(versions.isPreviousVersionToOld()).isTrue();
|
||||||
|
|
||||||
|
setVersion(directory, "1.12");
|
||||||
|
assertThat(versions.isPreviousVersionToOld()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnFalseForVersion160(@TempDirectory.TempDir Path directory) throws IOException {
|
||||||
|
setVersion(directory, "1.60");
|
||||||
|
assertThat(versions.isPreviousVersionToOld()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotFailIfVersionContainsLineBreak(@TempDirectory.TempDir Path directory) throws IOException {
|
||||||
|
setVersion(directory, "1.59\n");
|
||||||
|
assertThat(versions.isPreviousVersionToOld()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnFalseForVersionsNewerAs160(@TempDirectory.TempDir Path directory) throws IOException {
|
||||||
|
setVersion(directory, "1.61");
|
||||||
|
assertThat(versions.isPreviousVersionToOld()).isFalse();
|
||||||
|
|
||||||
|
setVersion(directory, "1.82");
|
||||||
|
assertThat(versions.isPreviousVersionToOld()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnFalseForNonExistingVersionFile(@TempDirectory.TempDir Path directory) {
|
||||||
|
setVersionFile(directory.resolve("version.txt"));
|
||||||
|
assertThat(versions.isPreviousVersionToOld()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldWriteNewVersion(@TempDirectory.TempDir Path directory) {
|
||||||
|
Path config = directory.resolve("config");
|
||||||
|
doReturn(config).when(contextProvider).resolve(Paths.get("config"));
|
||||||
|
doReturn("2.0.0").when(contextProvider).getVersion();
|
||||||
|
|
||||||
|
versions.writeNewVersion();
|
||||||
|
|
||||||
|
Path versionFile = config.resolve("version.txt");
|
||||||
|
assertThat(versionFile).exists().hasContent("2.0.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setVersion(Path directory, String version) throws IOException {
|
||||||
|
Path file = directory.resolve("version.txt");
|
||||||
|
Files.write(file, version.getBytes(StandardCharsets.UTF_8));
|
||||||
|
setVersionFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setVersionFile(Path file) {
|
||||||
|
doReturn(file).when(contextProvider).resolve(Paths.get("config", "version.txt"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user