mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-06-25 01:49:56 +02:00
Merge branch 'develop' of github.com:scm-manager/scm-manager into develop
This commit is contained in:
@@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Changed
|
||||
- Send mercurial hook callbacks over separate tcp socket instead of http ([#1416](https://github.com/scm-manager/scm-manager/pull/1416))
|
||||
- Implement mercurial cgi protocol as extension ([#1458](https://github.com/scm-manager/scm-manager/pull/1458))
|
||||
|
||||
### Fixed
|
||||
- Language detection of files with interpreter parameters e.g.: `#!/usr/bin/make -f` ([#1450](https://github.com/scm-manager/scm-manager/issues/1450))
|
||||
|
||||
@@ -21,12 +21,14 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.web.cgi;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -233,8 +235,20 @@ public abstract class AbstractCGIExecutor implements CGIExecutor
|
||||
this.workDirectory = workDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArgs(List<String> args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
protected List<String> args = Collections.emptyList();
|
||||
|
||||
/** Field description */
|
||||
protected int bufferSize;
|
||||
|
||||
|
||||
@@ -21,103 +21,112 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.web.cgi;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public interface CGIExecutor
|
||||
{
|
||||
public interface CGIExecutor {
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_AUTH_TYPE = "AUTH_TYPE";
|
||||
String ENV_AUTH_TYPE = "AUTH_TYPE";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_CONTENT_LENGTH = "CONTENT_LENGTH";
|
||||
String ENV_CONTENT_LENGTH = "CONTENT_LENGTH";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_CONTENT_TYPE = "CONTENT_TYPE";
|
||||
String ENV_CONTENT_TYPE = "CONTENT_TYPE";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_GATEWAY_INTERFACE = "GATEWAY_INTERFACE";
|
||||
String ENV_GATEWAY_INTERFACE = "GATEWAY_INTERFACE";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_HTTPS = "HTTPS";
|
||||
String ENV_HTTPS = "HTTPS";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_HTTPS_VALUE_OFF = "OFF";
|
||||
String ENV_HTTPS_VALUE_OFF = "OFF";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_HTTPS_VALUE_ON = "ON";
|
||||
String ENV_HTTPS_VALUE_ON = "ON";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_HTTP_HEADER_PREFIX = "HTTP_";
|
||||
String ENV_HTTP_HEADER_PREFIX = "HTTP_";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_PATH_INFO = "PATH_INFO";
|
||||
String ENV_PATH_INFO = "PATH_INFO";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_PATH_TRANSLATED = "PATH_TRANSLATED";
|
||||
String ENV_PATH_TRANSLATED = "PATH_TRANSLATED";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_QUERY_STRING = "QUERY_STRING";
|
||||
String ENV_QUERY_STRING = "QUERY_STRING";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_REMOTE_ADDR = "REMOTE_ADDR";
|
||||
String ENV_REMOTE_ADDR = "REMOTE_ADDR";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_REMOTE_HOST = "REMOTE_HOST";
|
||||
String ENV_REMOTE_HOST = "REMOTE_HOST";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_REMOTE_USER = "REMOTE_USER";
|
||||
String ENV_REMOTE_USER = "REMOTE_USER";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_REQUEST_METHOD = "REQUEST_METHOD";
|
||||
String ENV_REQUEST_METHOD = "REQUEST_METHOD";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_SCRIPT_FILENAME = "SCRIPT_FILENAME";
|
||||
String ENV_SCRIPT_FILENAME = "SCRIPT_FILENAME";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_SCRIPT_NAME = "SCRIPT_NAME";
|
||||
String ENV_SCRIPT_NAME = "SCRIPT_NAME";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_SERVER_NAME = "SERVER_NAME";
|
||||
String ENV_SERVER_NAME = "SERVER_NAME";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_SERVER_PORT = "SERVER_PORT";
|
||||
String ENV_SERVER_PORT = "SERVER_PORT";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_SERVER_PROTOCOL = "SERVER_PROTOCOL";
|
||||
String ENV_SERVER_PROTOCOL = "SERVER_PROTOCOL";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_SERVER_SOFTWARE = "SERVER_SOFTWARE";
|
||||
String ENV_SERVER_SOFTWARE = "SERVER_SOFTWARE";
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_SYSTEM_ROOT = "SystemRoot";
|
||||
String ENV_SYSTEM_ROOT = "SystemRoot";
|
||||
|
||||
/**
|
||||
* Content type header of response.
|
||||
* @since 2.12.0
|
||||
*/
|
||||
String RESPONSE_HEADER_CONTENT_TYPE = "Content-Type";
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #RESPONSE_HEADER_CONTENT_TYPE} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
String REPSONSE_HEADER_CONTENT_TYPE = RESPONSE_HEADER_CONTENT_TYPE;
|
||||
|
||||
/** Field description */
|
||||
public static final String REPSONSE_HEADER_CONTENT_TYPE = "Content-Type";
|
||||
String RESPONSE_HEADER_CONTENT_LENGTH = "Content-Length";
|
||||
|
||||
/** Field description */
|
||||
public static final String RESPONSE_HEADER_CONTENT_LENGTH = "Content-Length";
|
||||
String RESPONSE_HEADER_HTTP_PREFIX = "HTTP";
|
||||
|
||||
/** Field description */
|
||||
public static final String RESPONSE_HEADER_HTTP_PREFIX = "HTTP";
|
||||
String RESPONSE_HEADER_LOCATION = "Location";
|
||||
|
||||
/** Field description */
|
||||
public static final String RESPONSE_HEADER_LOCATION = "Location";
|
||||
|
||||
/** Field description */
|
||||
public static final String RESPONSE_HEADER_STATUS = "Status";
|
||||
String RESPONSE_HEADER_STATUS = "Status";
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
@@ -130,7 +139,7 @@ public interface CGIExecutor
|
||||
* @throws IOException
|
||||
* @throws ServletException
|
||||
*/
|
||||
public void execute(String cmd) throws IOException, ServletException;
|
||||
void execute(String cmd) throws IOException, ServletException;
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
@@ -140,7 +149,7 @@ public interface CGIExecutor
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getBufferSize();
|
||||
int getBufferSize();
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -148,7 +157,7 @@ public interface CGIExecutor
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public EnvList getEnvironment();
|
||||
EnvList getEnvironment();
|
||||
|
||||
/**
|
||||
* Returns the cgi exception handler.
|
||||
@@ -157,7 +166,7 @@ public interface CGIExecutor
|
||||
* @return cgi exception handler
|
||||
* @since 1.8
|
||||
*/
|
||||
public CGIExceptionHandler getExceptionHandler();
|
||||
CGIExceptionHandler getExceptionHandler();
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -165,7 +174,7 @@ public interface CGIExecutor
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getInterpreter();
|
||||
String getInterpreter();
|
||||
|
||||
/**
|
||||
* Returns the status code handler.
|
||||
@@ -174,7 +183,7 @@ public interface CGIExecutor
|
||||
* @return status code handler
|
||||
* @since 1.15
|
||||
*/
|
||||
public CGIStatusCodeHandler getStatusCodeHandler();
|
||||
CGIStatusCodeHandler getStatusCodeHandler();
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -182,7 +191,7 @@ public interface CGIExecutor
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public File getWorkDirectory();
|
||||
File getWorkDirectory();
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -191,7 +200,7 @@ public interface CGIExecutor
|
||||
* @return
|
||||
* @since 1.12
|
||||
*/
|
||||
public boolean isContentLengthWorkaround();
|
||||
boolean isContentLengthWorkaround();
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -199,7 +208,7 @@ public interface CGIExecutor
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isIgnoreExitCode();
|
||||
boolean isIgnoreExitCode();
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -207,7 +216,17 @@ public interface CGIExecutor
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isPassShellEnvironment();
|
||||
boolean isPassShellEnvironment();
|
||||
|
||||
/**
|
||||
* Returns command args as list.
|
||||
*
|
||||
* @return list of command args
|
||||
* @since 2.12.0
|
||||
*/
|
||||
default List<String> getArgs() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
@@ -217,7 +236,7 @@ public interface CGIExecutor
|
||||
*
|
||||
* @param bufferSize
|
||||
*/
|
||||
public void setBufferSize(int bufferSize);
|
||||
void setBufferSize(int bufferSize);
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -225,7 +244,7 @@ public interface CGIExecutor
|
||||
*
|
||||
* @param contentLengthWorkaround
|
||||
*/
|
||||
public void setContentLengthWorkaround(boolean contentLengthWorkaround);
|
||||
void setContentLengthWorkaround(boolean contentLengthWorkaround);
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -233,7 +252,7 @@ public interface CGIExecutor
|
||||
*
|
||||
* @param environment
|
||||
*/
|
||||
public void setEnvironment(EnvList environment);
|
||||
void setEnvironment(EnvList environment);
|
||||
|
||||
/**
|
||||
* Sets the cgi exception handler.
|
||||
@@ -242,7 +261,7 @@ public interface CGIExecutor
|
||||
* @param exceptionHandler cgi exception handler
|
||||
* @since 1.8
|
||||
*/
|
||||
public void setExceptionHandler(CGIExceptionHandler exceptionHandler);
|
||||
void setExceptionHandler(CGIExceptionHandler exceptionHandler);
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -250,7 +269,7 @@ public interface CGIExecutor
|
||||
*
|
||||
* @param ignoreExitCode
|
||||
*/
|
||||
public void setIgnoreExitCode(boolean ignoreExitCode);
|
||||
void setIgnoreExitCode(boolean ignoreExitCode);
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -258,7 +277,7 @@ public interface CGIExecutor
|
||||
*
|
||||
* @param interpreter
|
||||
*/
|
||||
public void setInterpreter(String interpreter);
|
||||
void setInterpreter(String interpreter);
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -266,7 +285,7 @@ public interface CGIExecutor
|
||||
*
|
||||
* @param passShellEnvironment
|
||||
*/
|
||||
public void setPassShellEnvironment(boolean passShellEnvironment);
|
||||
void setPassShellEnvironment(boolean passShellEnvironment);
|
||||
|
||||
/**
|
||||
* Sets the status code handler.
|
||||
@@ -275,7 +294,7 @@ public interface CGIExecutor
|
||||
* @param statusCodeHandler the handler to set
|
||||
* @since 1.15
|
||||
*/
|
||||
public void setStatusCodeHandler(CGIStatusCodeHandler statusCodeHandler);
|
||||
void setStatusCodeHandler(CGIStatusCodeHandler statusCodeHandler);
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -283,5 +302,13 @@ public interface CGIExecutor
|
||||
*
|
||||
* @param workDirectory
|
||||
*/
|
||||
public void setWorkDirectory(File workDirectory);
|
||||
void setWorkDirectory(File workDirectory);
|
||||
|
||||
/**
|
||||
* Set command arguments.
|
||||
* @param args command arguments
|
||||
* @since 2.12.0
|
||||
*/
|
||||
default void setArgs(List<String> args) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,76 +21,51 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.web.cgi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ForwardingMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class EnvList
|
||||
{
|
||||
public class EnvList {
|
||||
|
||||
/** Field description */
|
||||
private static final ImmutableSet<String> SENSITIVE =
|
||||
ImmutableSet.of("HTTP_AUTHORIZATION", "SCM_CHALLENGE", "SCM_CREDENTIALS");
|
||||
ImmutableSet.of("HTTP_AUTHORIZATION", "SCM_CHALLENGE", "SCM_CREDENTIALS", "SCM_BEARER_TOKEN");
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
private final Map<String, String> envMap;
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
public EnvList()
|
||||
{
|
||||
public EnvList() {
|
||||
envMap = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param l
|
||||
*/
|
||||
public EnvList(EnvList l)
|
||||
{
|
||||
envMap = new HashMap<>(l.envMap);
|
||||
public EnvList(EnvList list) {
|
||||
envMap = new HashMap<>(list.envMap);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns environment as mutable map.
|
||||
* Set a name/value pair, null values will be treated as an empty String
|
||||
*
|
||||
* @return environment as mutable map
|
||||
* @since 1.31
|
||||
* @param name name of environment variable
|
||||
* @param value value of environment variable
|
||||
*/
|
||||
public Map<String, String> asMutableMap()
|
||||
{
|
||||
return new MapDelegate(envMap);
|
||||
public void set(String name, String value) {
|
||||
envMap.put(name, Strings.nullToEmpty(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
* Return {@code true} if the list contains an environment variable with the given key.
|
||||
*
|
||||
*
|
||||
* @param key
|
||||
*
|
||||
* @return
|
||||
* @param key name of environment variable
|
||||
* @return {@code true} if contains environment variable
|
||||
*/
|
||||
public boolean containsKey(String key)
|
||||
{
|
||||
@@ -98,143 +73,69 @@ public class EnvList
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
* Representation suitable for passing to exec.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
* @return array of environment variables
|
||||
* @since 2.12.0
|
||||
*/
|
||||
public String[] asArray() {
|
||||
return envMap.entrySet()
|
||||
.stream()
|
||||
.map(e -> e.getKey() + "=" + e.getValue())
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation suitable for passing to process builder.
|
||||
*
|
||||
* @return environment as immutable map
|
||||
* @since 2.12.0
|
||||
*/
|
||||
public Map<String, String> asMap() {
|
||||
return Collections.unmodifiableMap(envMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
public String toString() {
|
||||
String s = System.getProperty("line.separator");
|
||||
StringBuilder out = new StringBuilder("Environment:");
|
||||
|
||||
Iterator<String> it = envMap.values().iterator();
|
||||
|
||||
String v;
|
||||
|
||||
while (it.hasNext())
|
||||
{
|
||||
v = converSensitive(it.next());
|
||||
out.append(s).append(" ").append(v);
|
||||
for (Map.Entry<String, String> e : envMap.entrySet()) {
|
||||
out
|
||||
.append(s).append(" ")
|
||||
.append(e.getKey()).append("=").append(convertSensitive(e.getKey(), e.getValue()));
|
||||
}
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
private String convertSensitive(String name, String value) {
|
||||
if (SENSITIVE.contains(name)) {
|
||||
return "(is set)";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get representation suitable for passing to exec.
|
||||
*
|
||||
* @return
|
||||
* @return array of environment variables
|
||||
* @deprecated use {@link #asArray()} instead
|
||||
*/
|
||||
public String[] getEnvArray()
|
||||
{
|
||||
return envMap.values().toArray(new String[envMap.size()]);
|
||||
@Deprecated
|
||||
public String[] getEnvArray() {
|
||||
return asArray();
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Set a name/value pair, null values will be treated as an empty String
|
||||
* Returns environment as mutable map.
|
||||
*
|
||||
* @param name
|
||||
* @param value
|
||||
* @return environment as mutable map
|
||||
* @since 1.31
|
||||
*
|
||||
* @deprecated the environment should only be modified by {@link #set(String, String)}.
|
||||
* Of a {@link Map} is required, a immutable {@link Map} can be created with {@link #asMap()}.
|
||||
*/
|
||||
public void set(String name, String value)
|
||||
{
|
||||
envMap.put(name, name.concat("=").concat(Util.nonNull(value)));
|
||||
@Deprecated
|
||||
public Map<String, String> asMutableMap() {
|
||||
return envMap;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param v
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String converSensitive(String v)
|
||||
{
|
||||
String result = v;
|
||||
|
||||
for (String s : SENSITIVE)
|
||||
{
|
||||
if (v.startsWith(s))
|
||||
{
|
||||
result = s.concat("=(is set)");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Class description
|
||||
*
|
||||
*
|
||||
* @version Enter version here..., 13/05/15
|
||||
* @author Enter your name here...
|
||||
*/
|
||||
private static class MapDelegate extends ForwardingMap<String, String>
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param delegate
|
||||
*/
|
||||
private MapDelegate(Map<String, String> delegate)
|
||||
{
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
//~--- methods ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String put(String key, String value)
|
||||
{
|
||||
return super.put(key, key.concat("=").concat(Strings.nullToEmpty(value)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected Map<String, String> delegate()
|
||||
{
|
||||
return delegate;
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private Map<String, String> delegate;
|
||||
}
|
||||
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private Map<String, String> envMap;
|
||||
}
|
||||
|
||||
@@ -21,38 +21,39 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.web.cgi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
class EnvListTest {
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class EnvListTest
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testToString()
|
||||
{
|
||||
EnvList envList = new EnvList();
|
||||
void shouldNotPrintAuthorizationValue() {
|
||||
EnvList env = new EnvList();
|
||||
env.set("HTTP_AUTHORIZATION", "Basic xxx");
|
||||
env.set("SOME_OTHER", "other");
|
||||
env.set("SCM_BEARER_TOKEN", "secret");
|
||||
|
||||
envList.set("HTTP_AUTHORIZATION", "Basic xxx");
|
||||
envList.set("SOME_OTHER", "other");
|
||||
String value = env.toString();
|
||||
|
||||
String value = envList.toString();
|
||||
assertThat(value)
|
||||
.contains("SOME_OTHER=other")
|
||||
.contains("HTTP_AUTHORIZATION=(is set)")
|
||||
.contains("SCM_BEARER_TOKEN=(is set)")
|
||||
.doesNotContain("HTTP_AUTHORIZATION=Basic xxx")
|
||||
.doesNotContain("SCM_BEARER_TOKEN=secret");
|
||||
}
|
||||
|
||||
assertTrue(value.contains("SOME_OTHER=other"));
|
||||
assertFalse(value.contains("HTTP_AUTHORIZATION=Basic xxx"));
|
||||
assertTrue(value.contains("HTTP_AUTHORIZATION=(is set)"));
|
||||
@Test
|
||||
void shouldReturnAsArray() {
|
||||
EnvList env = new EnvList();
|
||||
env.set("SPACESHIPT", "Heart of Gold");
|
||||
env.set("DOMAIN", "hitchhiker.com");
|
||||
|
||||
assertThat(env.getEnvArray())
|
||||
.contains("SPACESHIPT=Heart of Gold")
|
||||
.contains("DOMAIN=hitchhiker.com");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +41,6 @@ public class HgConfigDto extends HalRepresentation implements UpdateHgConfigDto
|
||||
|
||||
private String encoding;
|
||||
private String hgBinary;
|
||||
private String pythonBinary;
|
||||
private String pythonPath;
|
||||
private boolean useOptimizedBytecode;
|
||||
private boolean showRevisionInId;
|
||||
private boolean enableHttpPostArgs;
|
||||
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import sonia.scm.config.ConfigurationPermissions;
|
||||
import sonia.scm.installer.HgInstallerFactory;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.web.HgVndMediaType;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
|
||||
public class HgConfigInstallationsResource {
|
||||
|
||||
public static final String PATH_HG = "hg";
|
||||
public static final String PATH_PYTHON = "python";
|
||||
private final HgConfigInstallationsToDtoMapper hgConfigInstallationsToDtoMapper;
|
||||
|
||||
@Inject
|
||||
public HgConfigInstallationsResource(HgConfigInstallationsToDtoMapper hgConfigInstallationsToDtoMapper) {
|
||||
this.hgConfigInstallationsToDtoMapper = hgConfigInstallationsToDtoMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hg installations.
|
||||
*/
|
||||
@GET
|
||||
@Path(PATH_HG)
|
||||
@Produces(HgVndMediaType.INSTALLATIONS)
|
||||
@Operation(summary = "Hg installations", description = "Returns the mercurial installations.", tags = "Mercurial")
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "success",
|
||||
content = @Content(
|
||||
mediaType = HgVndMediaType.INSTALLATIONS,
|
||||
schema = @Schema(implementation = HgConfigInstallationsDto.class)
|
||||
)
|
||||
)
|
||||
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:read:hg\" privilege")
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "internal server error",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
))
|
||||
public HalRepresentation getHgInstallations() {
|
||||
|
||||
ConfigurationPermissions.read(HgConfig.PERMISSION).check();
|
||||
|
||||
return hgConfigInstallationsToDtoMapper.map(
|
||||
HgInstallerFactory.createInstaller().getHgInstallations(), PATH_HG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the python installations.
|
||||
*/
|
||||
@GET
|
||||
@Path(PATH_PYTHON)
|
||||
@Produces(HgVndMediaType.INSTALLATIONS)
|
||||
@Operation(summary = "Python installations", description = "Returns the python installations.", tags = "Mercurial")
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "success",
|
||||
content = @Content(
|
||||
mediaType = HgVndMediaType.INSTALLATIONS,
|
||||
schema = @Schema(implementation = HgConfigInstallationsDto.class)
|
||||
)
|
||||
)
|
||||
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:read:hg\" privilege")
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "internal server error",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
))
|
||||
public HalRepresentation getPythonInstallations() {
|
||||
|
||||
ConfigurationPermissions.read(HgConfig.PERMISSION).check();
|
||||
|
||||
return hgConfigInstallationsToDtoMapper.map(
|
||||
HgInstallerFactory.createInstaller().getPythonInstallations(), PATH_PYTHON);
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.config.ConfigurationPermissions;
|
||||
import sonia.scm.installer.HgInstallerFactory;
|
||||
import sonia.scm.installer.HgPackage;
|
||||
import sonia.scm.installer.HgPackageReader;
|
||||
import sonia.scm.net.ahc.AdvancedHttpClient;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
import sonia.scm.web.HgVndMediaType;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
public class HgConfigPackageResource {
|
||||
|
||||
private final HgPackageReader pkgReader;
|
||||
private final AdvancedHttpClient client;
|
||||
private final HgRepositoryHandler handler;
|
||||
private final HgConfigPackagesToDtoMapper configPackageCollectionToDtoMapper;
|
||||
|
||||
@Inject
|
||||
public HgConfigPackageResource(HgPackageReader pkgReader, AdvancedHttpClient client, HgRepositoryHandler handler,
|
||||
HgConfigPackagesToDtoMapper hgConfigPackagesToDtoMapper) {
|
||||
this.pkgReader = pkgReader;
|
||||
this.client = client;
|
||||
this.handler = handler;
|
||||
this.configPackageCollectionToDtoMapper = hgConfigPackagesToDtoMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all mercurial packages.
|
||||
*/
|
||||
@GET
|
||||
@Path("")
|
||||
@Produces(HgVndMediaType.PACKAGES)
|
||||
@Operation(summary = "Hg configuration packages", description = "Returns all mercurial packages.", tags = "Mercurial")
|
||||
@ApiResponse(
|
||||
responseCode = "204",
|
||||
description = "update success"
|
||||
)
|
||||
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:read:hg\" privilege")
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "internal server error",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
))
|
||||
public HalRepresentation getPackages() {
|
||||
|
||||
ConfigurationPermissions.read(HgConfig.PERMISSION).check();
|
||||
|
||||
return configPackageCollectionToDtoMapper.map(pkgReader.getPackages());
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a mercurial package
|
||||
*
|
||||
* @param pkgId Identifier of the package to install
|
||||
*/
|
||||
@PUT
|
||||
@Path("{pkgId}")
|
||||
@Operation(summary = "Modifies hg configuration package", description = "Installs a mercurial package.", tags = "Mercurial")
|
||||
@ApiResponse(
|
||||
responseCode = "204",
|
||||
description = "update success"
|
||||
)
|
||||
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:write:hg\" privilege")
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "no package found for id",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
))
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "internal server error",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
))
|
||||
public Response installPackage(@PathParam("pkgId") String pkgId) {
|
||||
Response response;
|
||||
|
||||
ConfigurationPermissions.write(HgConfig.PERMISSION).check();
|
||||
|
||||
HgPackage pkg = pkgReader.getPackage(pkgId);
|
||||
|
||||
if (pkg != null) {
|
||||
if (HgInstallerFactory.createInstaller()
|
||||
.installPackage(client, handler, SCMContext.getContext().getBaseDirectory(), pkg)) {
|
||||
response = Response.noContent().build();
|
||||
} else {
|
||||
response = Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
} else {
|
||||
response = Response.status(Response.Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.Links;
|
||||
import lombok.Getter;
|
||||
import org.mapstruct.AfterMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import sonia.scm.installer.HgPackage;
|
||||
import sonia.scm.installer.HgPackages;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
import static de.otto.edison.hal.Links.linkingTo;
|
||||
|
||||
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
|
||||
@SuppressWarnings("squid:S3306")
|
||||
@Mapper
|
||||
public abstract class HgConfigPackagesToDtoMapper {
|
||||
|
||||
@Inject
|
||||
private ScmPathInfoStore scmPathInfoStore;
|
||||
|
||||
public HgConfigPackagesDto map(HgPackages hgpackages) {
|
||||
return map(new HgPackagesNonIterable(hgpackages));
|
||||
}
|
||||
|
||||
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||
/* Favor warning "Unmapped target property: "attributes", to packages[].hgConfigTemplate"
|
||||
Over error "Unknown property "packages[].hgConfigTemplate.attributes"
|
||||
@Mapping(target = "packages[].hgConfigTemplate.attributes", ignore = true) // Also not for nested DTOs
|
||||
*/
|
||||
protected abstract HgConfigPackagesDto map(HgPackagesNonIterable hgPackagesNonIterable);
|
||||
|
||||
@AfterMapping
|
||||
void appendLinks(@MappingTarget HgConfigPackagesDto target) {
|
||||
Links.Builder linksBuilder = linkingTo().self(createSelfLink());
|
||||
target.add(linksBuilder.build());
|
||||
}
|
||||
|
||||
private String createSelfLink() {
|
||||
LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), HgConfigResource.class);
|
||||
return linkBuilder.method("getPackagesResource").parameters().href();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unfortunately, HgPackages is iterable, HgConfigPackagesDto does not need to be iterable and MapStruct refuses to
|
||||
* map an iterable to a non-iterable. So use this little non-iterable "proxy".
|
||||
*/
|
||||
@Getter
|
||||
static class HgPackagesNonIterable {
|
||||
private List<HgPackage> packages;
|
||||
|
||||
HgPackagesNonIterable(HgPackages hgPackages) {
|
||||
this.packages = hgPackages.getPackages();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||
@@ -60,21 +60,17 @@ public class HgConfigResource {
|
||||
private final HgConfigDtoToHgConfigMapper dtoToConfigMapper;
|
||||
private final HgConfigToHgConfigDtoMapper configToDtoMapper;
|
||||
private final HgRepositoryHandler repositoryHandler;
|
||||
private final Provider<HgConfigPackageResource> packagesResource;
|
||||
private final Provider<HgConfigAutoConfigurationResource> autoconfigResource;
|
||||
private final Provider<HgConfigInstallationsResource> installationsResource;
|
||||
|
||||
@Inject
|
||||
public HgConfigResource(HgConfigDtoToHgConfigMapper dtoToConfigMapper, HgConfigToHgConfigDtoMapper configToDtoMapper,
|
||||
HgRepositoryHandler repositoryHandler, Provider<HgConfigPackageResource> packagesResource,
|
||||
Provider<HgConfigAutoConfigurationResource> autoconfigResource,
|
||||
Provider<HgConfigInstallationsResource> installationsResource) {
|
||||
public HgConfigResource(HgConfigDtoToHgConfigMapper dtoToConfigMapper,
|
||||
HgConfigToHgConfigDtoMapper configToDtoMapper,
|
||||
HgRepositoryHandler repositoryHandler,
|
||||
Provider<HgConfigAutoConfigurationResource> autoconfigResource) {
|
||||
this.dtoToConfigMapper = dtoToConfigMapper;
|
||||
this.configToDtoMapper = configToDtoMapper;
|
||||
this.repositoryHandler = repositoryHandler;
|
||||
this.packagesResource = packagesResource;
|
||||
this.autoconfigResource = autoconfigResource;
|
||||
this.installationsResource = installationsResource;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,7 +130,7 @@ public class HgConfigResource {
|
||||
schema = @Schema(implementation = UpdateHgConfigDto.class),
|
||||
examples = @ExampleObject(
|
||||
name = "Overwrites current configuration with this one.",
|
||||
value = "{\n \"disabled\":false,\n \"hgBinary\":\"hg\",\n \"pythonBinary\":\"python\",\n \"pythonPath\":\"\",\n \"encoding\":\"UTF-8\",\n \"useOptimizedBytecode\":false,\n \"showRevisionInId\":false,\n \"disableHookSSLValidation\":false,\n \"enableHttpPostArgs\":false\n}",
|
||||
value = "{\n \"disabled\":false,\n \"hgBinary\":\"hg\",\n \"encoding\":\"UTF-8\",\n \"showRevisionInId\":false,\n \"enableHttpPostArgs\":false\n}",
|
||||
summary = "Simple update configuration"
|
||||
)
|
||||
)
|
||||
@@ -165,18 +161,8 @@ public class HgConfigResource {
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
@Path("packages")
|
||||
public HgConfigPackageResource getPackagesResource() {
|
||||
return packagesResource.get();
|
||||
}
|
||||
|
||||
@Path("auto-configuration")
|
||||
public HgConfigAutoConfigurationResource getAutoConfigurationResource() {
|
||||
return autoconfigResource.get();
|
||||
}
|
||||
|
||||
@Path("installations")
|
||||
public HgConfigInstallationsResource getInstallationsResource() {
|
||||
return installationsResource.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,14 +29,8 @@ interface UpdateHgConfigDto {
|
||||
|
||||
String getHgBinary();
|
||||
|
||||
String getPythonBinary();
|
||||
|
||||
String getPythonPath();
|
||||
|
||||
String getEncoding();
|
||||
|
||||
boolean isUseOptimizedBytecode();
|
||||
|
||||
boolean isShowRevisionInId();
|
||||
|
||||
boolean isEnableHttpPostArgs();
|
||||
|
||||
@@ -21,30 +21,16 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
package sonia.scm.autoconfig;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import com.google.inject.AbstractModule;
|
||||
import sonia.scm.plugin.Extension;
|
||||
|
||||
import static de.otto.edison.hal.Links.linkingTo;
|
||||
|
||||
public class HgConfigInstallationsToDtoMapper {
|
||||
|
||||
private ScmPathInfoStore scmPathInfoStore;
|
||||
|
||||
@Inject
|
||||
public HgConfigInstallationsToDtoMapper(ScmPathInfoStore scmPathInfoStore) {
|
||||
this.scmPathInfoStore = scmPathInfoStore;
|
||||
}
|
||||
|
||||
public HgConfigInstallationsDto map(List<String> installations, String path) {
|
||||
return new HgConfigInstallationsDto(linkingTo().self(createSelfLink(path)).build(), installations);
|
||||
}
|
||||
|
||||
private String createSelfLink(String path) {
|
||||
LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), HgConfigResource.class);
|
||||
return linkBuilder.method("getInstallationsResource").parameters().href() + '/' + path;
|
||||
@Extension
|
||||
public class AutoConfigModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(AutoConfigurator.class).toProvider(AutoConfiguratorProvider.class);
|
||||
}
|
||||
}
|
||||
@@ -24,26 +24,10 @@
|
||||
|
||||
package sonia.scm.autoconfig;
|
||||
|
||||
import sonia.scm.Platform;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.util.SystemUtil;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface AutoConfigurator {
|
||||
|
||||
HgConfig configure();
|
||||
|
||||
HgConfig configure(Path hg);
|
||||
|
||||
static Optional<AutoConfigurator> get() {
|
||||
// at the moment we have only support for posix based systems
|
||||
Platform platform = SystemUtil.getPlatform();
|
||||
if (platform.isPosix()) {
|
||||
return Optional.of(new PosixAutoConfigurator(System.getenv()));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
void configure(HgConfig config);
|
||||
|
||||
}
|
||||
|
||||
@@ -21,42 +21,41 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import de.otto.edison.hal.Links;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
package sonia.scm.autoconfig;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import sonia.scm.Platform;
|
||||
import sonia.scm.repository.HgVerifier;
|
||||
import sonia.scm.util.SystemUtil;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public class HgConfigPackagesDto extends HalRepresentation {
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
private List<HgConfigPackageDto> packages;
|
||||
public class AutoConfiguratorProvider implements Provider<AutoConfigurator> {
|
||||
|
||||
private final HgVerifier verifier;
|
||||
private final Platform platform;
|
||||
|
||||
@Inject
|
||||
public AutoConfiguratorProvider(HgVerifier verifier) {
|
||||
this(verifier, SystemUtil.getPlatform());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
AutoConfiguratorProvider(HgVerifier verifier, Platform platform) {
|
||||
this.verifier = verifier;
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
||||
protected HalRepresentation add(Links links) {
|
||||
return super.add(links);
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public static class HgConfigPackageDto {
|
||||
|
||||
private String arch;
|
||||
private HgConfigDto hgConfigTemplate;
|
||||
private String hgVersion;
|
||||
private String id;
|
||||
private String platform;
|
||||
private String pythonVersion;
|
||||
private long size;
|
||||
private String url;
|
||||
public AutoConfigurator get() {
|
||||
if (platform.isPosix()) {
|
||||
return new PosixAutoConfigurator(verifier, System.getenv());
|
||||
} else if (platform.isWindows()) {
|
||||
return new WindowsAutoConfigurator(verifier, new WindowsRegistry(), System.getenv());
|
||||
} else {
|
||||
return new NoOpAutoConfigurator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,51 +21,23 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.installer;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
package sonia.scm.autoconfig;
|
||||
|
||||
import sonia.scm.util.SystemUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public final class HgInstallerFactory
|
||||
{
|
||||
public class NoOpAutoConfigurator implements AutoConfigurator {
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
private HgInstallerFactory() {}
|
||||
private static final Logger LOG = LoggerFactory.getLogger(NoOpAutoConfigurator.class);
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
NoOpAutoConfigurator() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static HgInstaller createInstaller()
|
||||
{
|
||||
HgInstaller installer = null;
|
||||
|
||||
if (SystemUtil.isWindows())
|
||||
{
|
||||
installer = new WindowsHgInstaller();
|
||||
}
|
||||
else if (SystemUtil.isMac())
|
||||
{
|
||||
installer = new MacOSHgInstaller();
|
||||
}
|
||||
else
|
||||
{
|
||||
installer = new UnixHgInstaller();
|
||||
}
|
||||
|
||||
return installer;
|
||||
@Override
|
||||
public void configure(HgConfig config) {
|
||||
// if we do not know the environment, we could not configure mercurial
|
||||
LOG.debug("no mercurial autoconfiguration available on this platform");
|
||||
}
|
||||
}
|
||||
@@ -27,19 +27,12 @@ package sonia.scm.autoconfig;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.MoreFiles;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.HgVerifier;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -58,155 +51,40 @@ public class PosixAutoConfigurator implements AutoConfigurator {
|
||||
"/opt/local/bin"
|
||||
);
|
||||
|
||||
private final HgVerifier verifier;
|
||||
private final Set<String> fsPaths;
|
||||
|
||||
private Executor executor = (Path binary, String... args) -> {
|
||||
ProcessBuilder builder = new ProcessBuilder(
|
||||
Lists.asList(binary.toString(), args).toArray(new String[0])
|
||||
);
|
||||
Process process = builder.start();
|
||||
int rc = process.waitFor();
|
||||
if (rc != 0) {
|
||||
throw new IOException(binary.toString() + " failed with return code " + rc);
|
||||
}
|
||||
return process.getInputStream();
|
||||
};
|
||||
|
||||
PosixAutoConfigurator(Map<String, String> env) {
|
||||
this(env, ADDITIONAL_PATH);
|
||||
PosixAutoConfigurator(HgVerifier verifier, Map<String, String> env) {
|
||||
this(verifier, env, ADDITIONAL_PATH);
|
||||
}
|
||||
|
||||
PosixAutoConfigurator(Map<String, String> env, List<String> additionalPaths) {
|
||||
@VisibleForTesting
|
||||
PosixAutoConfigurator(HgVerifier verifier, Map<String, String> env, List<String> additionalPaths) {
|
||||
this.verifier = verifier;
|
||||
String path = env.getOrDefault("PATH", "");
|
||||
fsPaths = new LinkedHashSet<>();
|
||||
fsPaths.addAll(Splitter.on(File.pathSeparator).splitToList(path));
|
||||
fsPaths.addAll(additionalPaths);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setExecutor(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HgConfig configure() {
|
||||
Optional<Path> hg = findInPath("hg");
|
||||
public void configure(HgConfig config) {
|
||||
Optional<Path> hg = findInPath();
|
||||
if (hg.isPresent()) {
|
||||
return configure(hg.get());
|
||||
config.setHgBinary(hg.get().toAbsolutePath().toString());
|
||||
} else {
|
||||
LOG.warn("could not find valid mercurial installation");
|
||||
}
|
||||
return new HgConfig();
|
||||
}
|
||||
|
||||
private Optional<Path> findInPath(String binary) {
|
||||
private Optional<Path> findInPath() {
|
||||
for (String directory : fsPaths) {
|
||||
Path binaryPath = Paths.get(directory, binary);
|
||||
if (Files.exists(binaryPath)) {
|
||||
Path binaryPath = Paths.get(directory, "hg");
|
||||
if (verifier.isValid(binaryPath)) {
|
||||
return Optional.of(binaryPath);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
private Optional<Path> findModulePath(Path hg) {
|
||||
if (!Files.isExecutable(hg)) {
|
||||
LOG.warn("{} is not executable", hg);
|
||||
return Optional.empty();
|
||||
}
|
||||
try {
|
||||
InputStream debuginstall = executor.execute(hg, "debuginstall");
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(debuginstall))) {
|
||||
while (reader.ready()) {
|
||||
String line = reader.readLine();
|
||||
if (line.contains("installed modules")) {
|
||||
int start = line.indexOf("(");
|
||||
int end = line.indexOf(")");
|
||||
Path modulePath = Paths.get(line.substring(start + 1, end));
|
||||
if (Files.exists(modulePath)) {
|
||||
// installed modules contains the path to the mercurial module,
|
||||
// but we need the parent for the python path
|
||||
return Optional.of(modulePath.getParent());
|
||||
} else {
|
||||
LOG.warn("could not find module path at {}", modulePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
LOG.warn("failed to parse debuginstall of {}", hg);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.warn("interrupted during debuginstall parsing of {}", hg);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HgConfig configure(Path hg) {
|
||||
HgConfig config = new HgConfig();
|
||||
try {
|
||||
if (Files.exists(hg)) {
|
||||
configureWithExistingHg(hg, config);
|
||||
} else {
|
||||
LOG.warn("{} does not exists", hg);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warn("failed to read first line of {}", hg);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
private void configureWithExistingHg(Path hg, HgConfig config) throws IOException {
|
||||
config.setHgBinary(hg.toAbsolutePath().toString());
|
||||
Optional<Path> pythonFromShebang = findPythonFromShebang(hg);
|
||||
if (pythonFromShebang.isPresent()) {
|
||||
config.setPythonBinary(pythonFromShebang.get().toAbsolutePath().toString());
|
||||
} else {
|
||||
LOG.warn("could not find python from shebang, searching for python in path");
|
||||
Optional<Path> python = findInPath("python");
|
||||
if (!python.isPresent()) {
|
||||
LOG.warn("could not find python in path, searching for python3 instead");
|
||||
python = findInPath("python3");
|
||||
}
|
||||
if (python.isPresent()) {
|
||||
config.setPythonBinary(python.get().toAbsolutePath().toString());
|
||||
} else {
|
||||
LOG.warn("could not find python in path");
|
||||
}
|
||||
}
|
||||
|
||||
Optional<Path> modulePath = findModulePath(hg);
|
||||
if (modulePath.isPresent()) {
|
||||
config.setPythonPath(modulePath.get().toAbsolutePath().toString());
|
||||
} else {
|
||||
LOG.warn("could not find module path");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Optional<Path> findPythonFromShebang(Path hg) throws IOException {
|
||||
String shebang = MoreFiles.asCharSource(hg, StandardCharsets.UTF_8).readFirstLine();
|
||||
if (shebang != null && shebang.startsWith("#!")) {
|
||||
String substring = shebang.substring(2);
|
||||
String[] parts = substring.split("\\s+");
|
||||
if (parts.length > 1) {
|
||||
return findInPath(parts[1]);
|
||||
} else {
|
||||
Path python = Paths.get(parts[0]);
|
||||
if (Files.exists(python)) {
|
||||
return Optional.of(python);
|
||||
} else {
|
||||
LOG.warn("python binary from shebang {} does not exists", python);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG.warn("first line does not look like a shebang: {}", shebang);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface Executor {
|
||||
InputStream execute(Path binary, String... args) throws IOException, InterruptedException;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.autoconfig;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Splitter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.HgVerifier;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class WindowsAutoConfigurator implements AutoConfigurator {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WindowsAutoConfigurator.class);
|
||||
|
||||
@VisibleForTesting
|
||||
static final String REGISTRY_KEY_TORTOISE_HG = "HKEY_LOCAL_MACHINE\\Software\\TortoiseHg";
|
||||
@VisibleForTesting
|
||||
static final String REGISTRY_KEY_MERCURIAL = "HKEY_LOCAL_MACHINE\\Software\\Mercurial\\InstallDir";
|
||||
|
||||
private static final String[] REGISTRY_KEYS = {REGISTRY_KEY_TORTOISE_HG, REGISTRY_KEY_MERCURIAL};
|
||||
|
||||
@VisibleForTesting
|
||||
static final String BINARY_HG_EXE = "hg.exe";
|
||||
@VisibleForTesting
|
||||
static final String BINARY_HG_BAT = "hg.bat";
|
||||
|
||||
private static final String[] BINARIES = {BINARY_HG_EXE, BINARY_HG_BAT};
|
||||
|
||||
@VisibleForTesting
|
||||
static final String ENV_PATH = "Path";
|
||||
|
||||
private final HgVerifier verifier;
|
||||
private final WindowsRegistry registry;
|
||||
private final Map<String, String> env;
|
||||
|
||||
WindowsAutoConfigurator(HgVerifier verifier, WindowsRegistry registry, Map<String, String> env) {
|
||||
this.verifier = verifier;
|
||||
this.registry = registry;
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(HgConfig config) {
|
||||
Set<String> fsPaths = new LinkedHashSet<>(pathFromEnv());
|
||||
resolveRegistryKeys(fsPaths);
|
||||
|
||||
Optional<String> hg = findInPath(fsPaths);
|
||||
if (hg.isPresent()) {
|
||||
String hgBinary = hg.get();
|
||||
LOG.info("found hg at {}", hgBinary);
|
||||
config.setHgBinary(hgBinary);
|
||||
} else {
|
||||
LOG.warn("could not find valid mercurial installation");
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveRegistryKeys(Set<String> fsPaths) {
|
||||
for (String registryKey : REGISTRY_KEYS) {
|
||||
Optional<String> registryValue = registry.get(registryKey);
|
||||
if (registryValue.isPresent()) {
|
||||
String directory = registryValue.get();
|
||||
LOG.trace("resolved registry key {} to directory {}", registryKey, directory);
|
||||
fsPaths.add(directory);
|
||||
} else {
|
||||
LOG.trace("could not find value for registry key {}", registryKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<String> pathFromEnv() {
|
||||
String path = env.getOrDefault(ENV_PATH, "");
|
||||
LOG.trace("try to find hg in PATH {}", path);
|
||||
return Splitter.on(File.pathSeparator).splitToList(path);
|
||||
}
|
||||
|
||||
private Optional<String> findInPath(Set<String> fsPaths) {
|
||||
for (String directory : fsPaths) {
|
||||
Optional<String> binaryPath = findInDirectory(directory);
|
||||
if (binaryPath.isPresent()) {
|
||||
return binaryPath;
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private Optional<String> findInDirectory(String directory) {
|
||||
LOG.trace("check directory {} for mercurial installations", directory);
|
||||
for (String binary : BINARIES) {
|
||||
Path hg = Paths.get(directory, binary);
|
||||
if (verifier.isValid(hg)) {
|
||||
return Optional.of(hg.toAbsolutePath().toString());
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,25 +21,19 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import de.otto.edison.hal.Links;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
package sonia.scm.autoconfig;
|
||||
|
||||
import java.util.List;
|
||||
import sonia.scm.util.RegistryUtil;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class HgConfigInstallationsDto extends HalRepresentation {
|
||||
import java.util.Optional;
|
||||
|
||||
private List<String> paths;
|
||||
public class WindowsRegistry {
|
||||
|
||||
public HgConfigInstallationsDto(Links links, List<String> paths) {
|
||||
super(links);
|
||||
this.paths = paths;
|
||||
public Optional<String> get(String key) {
|
||||
return Optional.ofNullable(
|
||||
RegistryUtil.getRegistryValue(key)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.installer;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import sonia.scm.net.ahc.AdvancedHttpClient;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public abstract class AbstractHgInstaller implements HgInstaller
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param client
|
||||
* @param handler
|
||||
* @param baseDirectory
|
||||
* @param pkg
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean installPackage(AdvancedHttpClient client, HgRepositoryHandler handler,
|
||||
File baseDirectory, HgPackage pkg)
|
||||
{
|
||||
return new HgPackageInstaller(client, handler, baseDirectory,
|
||||
pkg).install();
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.installer;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.net.ahc.AdvancedHttpClient;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public interface HgInstaller
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param baseDirectory
|
||||
* @param config
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void install(File baseDirectory, HgConfig config) throws IOException;
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param client
|
||||
* @param handler
|
||||
* @param baseDirectory
|
||||
* @param pkg
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean installPackage(AdvancedHttpClient client,
|
||||
HgRepositoryHandler handler, File baseDirectory, HgPackage pkg);
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param baseDirectory
|
||||
* @param config
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void update(File baseDirectory, HgConfig config) throws IOException;
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> getHgInstallations();
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> getPythonInstallations();
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.installer;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.repository.HgConfig;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@XmlRootElement(name = "package")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class HgPackage
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getArch()
|
||||
{
|
||||
return arch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public HgConfig getHgConfigTemplate()
|
||||
{
|
||||
return hgConfigTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getHgVersion()
|
||||
{
|
||||
return hgVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getPlatform()
|
||||
{
|
||||
return platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getPythonVersion()
|
||||
{
|
||||
return pythonVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getSize()
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getUrl()
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param arch
|
||||
*/
|
||||
public void setArch(String arch)
|
||||
{
|
||||
this.arch = arch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param hgConfigTemplate
|
||||
*/
|
||||
public void setHgConfigTemplate(HgConfig hgConfigTemplate)
|
||||
{
|
||||
this.hgConfigTemplate = hgConfigTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param hgVersion
|
||||
*/
|
||||
public void setHgVersion(String hgVersion)
|
||||
{
|
||||
this.hgVersion = hgVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
public void setId(String id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param platform
|
||||
*/
|
||||
public void setPlatform(String platform)
|
||||
{
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param pythonVersion
|
||||
*/
|
||||
public void setPythonVersion(String pythonVersion)
|
||||
{
|
||||
this.pythonVersion = pythonVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param size
|
||||
*/
|
||||
public void setSize(long size)
|
||||
{
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
public void setUrl(String url)
|
||||
{
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private String arch;
|
||||
|
||||
/** Field description */
|
||||
@XmlElement(name = "hg-config-template")
|
||||
private HgConfig hgConfigTemplate;
|
||||
|
||||
/** Field description */
|
||||
@XmlElement(name = "hg-version")
|
||||
private String hgVersion;
|
||||
|
||||
/** Field description */
|
||||
private String id;
|
||||
|
||||
/** Field description */
|
||||
private String platform;
|
||||
|
||||
/** Field description */
|
||||
@XmlElement(name = "python-version")
|
||||
private String pythonVersion;
|
||||
|
||||
/** Field description */
|
||||
private long size;
|
||||
|
||||
/** Field description */
|
||||
private String url;
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.installer;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.io.ZipUnArchiver;
|
||||
import sonia.scm.net.ahc.AdvancedHttpClient;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
import sonia.scm.repository.HgWindowsPackageFix;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class HgPackageInstaller implements Runnable
|
||||
{
|
||||
|
||||
/** the logger for HgPackageInstaller */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(HgPackageInstaller.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param client
|
||||
* @param handler
|
||||
* @param baseDirectory
|
||||
* @param pkg
|
||||
*/
|
||||
public HgPackageInstaller(AdvancedHttpClient client,
|
||||
HgRepositoryHandler handler, File baseDirectory, HgPackage pkg)
|
||||
{
|
||||
this.client = client;
|
||||
this.handler = handler;
|
||||
this.baseDirectory = baseDirectory;
|
||||
this.pkg = pkg;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean install()
|
||||
{
|
||||
boolean success = false;
|
||||
File downloadedFile = downloadFile();
|
||||
|
||||
if ((downloadedFile != null) && downloadedFile.exists())
|
||||
{
|
||||
File directory = extractPackage(downloadedFile);
|
||||
|
||||
if ((directory != null) && directory.exists())
|
||||
{
|
||||
updateConfig(directory);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (!install())
|
||||
{
|
||||
logger.error("installation of pkg {} failed", pkg.getId());
|
||||
}
|
||||
else if (logger.isInfoEnabled())
|
||||
{
|
||||
logger.info("successfully installed pkg {}", pkg.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private File downloadFile()
|
||||
{
|
||||
File file = null;
|
||||
InputStream input = null;
|
||||
OutputStream output = null;
|
||||
|
||||
try
|
||||
{
|
||||
file = File.createTempFile("scm-hg-", ".pkg");
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("download package to {}", file.getAbsolutePath());
|
||||
}
|
||||
|
||||
// TODO error handling
|
||||
input = client.get(pkg.getUrl()).request().contentAsStream();
|
||||
output = new FileOutputStream(file);
|
||||
IOUtil.copy(input, output);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.error("could not downlaod file ".concat(pkg.getUrl()), ex);
|
||||
file = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(input);
|
||||
IOUtil.close(output);
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param file
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private File extractPackage(File file)
|
||||
{
|
||||
File directory = new File(baseDirectory,
|
||||
"pkg".concat(File.separator).concat(pkg.getId()));
|
||||
|
||||
IOUtil.mkdirs(directory);
|
||||
|
||||
try
|
||||
{
|
||||
IOUtil.extract(file, directory, ZipUnArchiver.EXTENSION);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
directory = null;
|
||||
logger.error("could not extract pacakge ".concat(pkg.getId()), ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
// delete temp file
|
||||
try
|
||||
{
|
||||
IOUtil.delete(file, true);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.error(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param directory
|
||||
*/
|
||||
private void updateConfig(File directory)
|
||||
{
|
||||
String path = directory.getAbsolutePath();
|
||||
HgConfig template = pkg.getHgConfigTemplate();
|
||||
HgConfig config = handler.getConfig();
|
||||
|
||||
config.setHgBinary(getTemplateValue(template.getHgBinary(), path));
|
||||
config.setPythonBinary(getTemplateValue(template.getPythonBinary(), path));
|
||||
config.setPythonPath(getTemplateValue(template.getPythonPath(), path));
|
||||
config.setUseOptimizedBytecode(template.isUseOptimizedBytecode());
|
||||
|
||||
// fix wrong hg.bat
|
||||
HgWindowsPackageFix.fixHgPackage(SCMContext.getContext(), config);
|
||||
|
||||
handler.storeConfig();
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param template
|
||||
* @param path
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String getTemplateValue(String template, String path)
|
||||
{
|
||||
String result = null;
|
||||
|
||||
if (template != null)
|
||||
{
|
||||
result = MessageFormat.format(template, path);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private File baseDirectory;
|
||||
|
||||
/** Field description */
|
||||
private AdvancedHttpClient client;
|
||||
|
||||
/** Field description */
|
||||
private HgRepositoryHandler handler;
|
||||
|
||||
/** Field description */
|
||||
private HgPackage pkg;
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.installer;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.PlatformType;
|
||||
import sonia.scm.cache.Cache;
|
||||
import sonia.scm.cache.CacheManager;
|
||||
import sonia.scm.net.ahc.AdvancedHttpClient;
|
||||
import sonia.scm.util.SystemUtil;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class HgPackageReader
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
public static final String CACHENAME = "sonia.scm.hg.packages";
|
||||
|
||||
/** Field description */
|
||||
public static final String PACKAGEURL =
|
||||
"http://download.scm-manager.org/pkg/mercurial/packages.xml";
|
||||
|
||||
/** the logger for HgPackageReader */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(HgPackageReader.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param cacheManager
|
||||
* @param httpClient
|
||||
*/
|
||||
@Inject
|
||||
public HgPackageReader(CacheManager cacheManager, AdvancedHttpClient httpClient)
|
||||
{
|
||||
this.cache = cacheManager.getCache(CACHENAME);
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public HgPackage getPackage(String id)
|
||||
{
|
||||
HgPackage pkg = null;
|
||||
|
||||
for (HgPackage p : getPackages())
|
||||
{
|
||||
if (id.equals(p.getId()))
|
||||
{
|
||||
pkg = p;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pkg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public HgPackages getPackages()
|
||||
{
|
||||
HgPackages packages = cache.get(HgPackages.class.getName());
|
||||
|
||||
if (packages == null)
|
||||
{
|
||||
packages = getRemptePackages();
|
||||
filterPackage(packages);
|
||||
cache.put(HgPackages.class.getName(), packages);
|
||||
}
|
||||
|
||||
return packages;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param packages
|
||||
*/
|
||||
private void filterPackage(HgPackages packages)
|
||||
{
|
||||
List<HgPackage> pkgList = new ArrayList<>();
|
||||
|
||||
for (HgPackage pkg : packages)
|
||||
{
|
||||
boolean add = true;
|
||||
|
||||
if (Util.isNotEmpty(pkg.getPlatform()))
|
||||
{
|
||||
PlatformType pt = PlatformType.createPlatformType(pkg.getPlatform());
|
||||
|
||||
if (SystemUtil.getPlatform().getType() != pt)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("reject package {}, because of wrong platform {}",
|
||||
pkg.getId(), pkg.getPlatform());
|
||||
}
|
||||
|
||||
add = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (add && Util.isNotEmpty(pkg.getArch()))
|
||||
{
|
||||
if (!SystemUtil.getArch().equals(pkg.getArch()))
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("reject package {}, because of wrong arch {}",
|
||||
pkg.getId(), pkg.getArch());
|
||||
}
|
||||
|
||||
add = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (add)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("added HgPackage {}", pkg.getId());
|
||||
}
|
||||
|
||||
pkgList.add(pkg);
|
||||
}
|
||||
}
|
||||
|
||||
packages.setPackages(pkgList);
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private HgPackages getRemptePackages()
|
||||
{
|
||||
if (logger.isInfoEnabled())
|
||||
{
|
||||
logger.info("fetch HgPackages from {}", PACKAGEURL);
|
||||
}
|
||||
|
||||
HgPackages packages = null;
|
||||
|
||||
try
|
||||
{
|
||||
//J-
|
||||
packages = httpClient.get(PACKAGEURL)
|
||||
.request()
|
||||
.contentFromXml(HgPackages.class);
|
||||
//J+
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.error("could not read HgPackages from ".concat(PACKAGEURL), ex);
|
||||
}
|
||||
|
||||
if (packages == null)
|
||||
{
|
||||
packages = new HgPackages();
|
||||
packages.setPackages(new ArrayList<>());
|
||||
}
|
||||
|
||||
return packages;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final Cache<String, HgPackages> cache;
|
||||
|
||||
/** Field description */
|
||||
private final AdvancedHttpClient httpClient;
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.installer;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@XmlRootElement(name = "packages")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class HgPackages implements Iterable<HgPackage>
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Iterator<HgPackage> iterator()
|
||||
{
|
||||
return packages.iterator();
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<HgPackage> getPackages()
|
||||
{
|
||||
return packages;
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param packages
|
||||
*/
|
||||
public void setPackages(List<HgPackage> packages)
|
||||
{
|
||||
this.packages = packages;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
@XmlElement(name = "package")
|
||||
private List<HgPackage> packages;
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.installer;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.io.DirectoryFileFilter;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @deprecated use {@link sonia.scm.autoconfig.AutoConfigurator}
|
||||
*/
|
||||
@Deprecated
|
||||
public class MacOSHgInstaller extends UnixHgInstaller
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
public static final String ENV_PATH = "PATH";
|
||||
|
||||
/** Field description */
|
||||
public static final String PATH_HG = "hg";
|
||||
|
||||
/** Field description */
|
||||
public static final String PATH_HG_BREW = "/usr/local/bin/hg";
|
||||
|
||||
/** Field description */
|
||||
public static final String PATH_HG_BREW_INSTALLATION =
|
||||
"/usr/local/Cellar/mercurial";
|
||||
|
||||
/** Field description */
|
||||
public static final String PATH_PYTHON = "python";
|
||||
|
||||
/** the logger for MacOSHgInstaller */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(MacOSHgInstaller.class);
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param baseDirectory
|
||||
* @param config
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void install(File baseDirectory, HgConfig config) throws IOException
|
||||
{
|
||||
super.install(baseDirectory, config);
|
||||
|
||||
String hg = config.getHgBinary();
|
||||
|
||||
if (PATH_HG.equals(hg))
|
||||
{
|
||||
hg = resolvePath(hg);
|
||||
}
|
||||
|
||||
if (PATH_HG_BREW.equals(hg))
|
||||
{
|
||||
File file = new File(PATH_HG_BREW);
|
||||
|
||||
file = file.getCanonicalFile();
|
||||
|
||||
if (file.getAbsolutePath().startsWith(PATH_HG_BREW_INSTALLATION))
|
||||
{
|
||||
useHomebrewInstallation(config, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param parent
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private File findPythonDirectory(File parent)
|
||||
{
|
||||
File pythonDirectory = null;
|
||||
|
||||
for (File d : parent.listFiles(DirectoryFileFilter.instance))
|
||||
{
|
||||
if (d.getName().startsWith("python"))
|
||||
{
|
||||
pythonDirectory = d;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pythonDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param binaryName
|
||||
* @return
|
||||
*/
|
||||
private String resolvePath(String binaryName)
|
||||
{
|
||||
String binary = binaryName;
|
||||
|
||||
try
|
||||
{
|
||||
String path = System.getenv(ENV_PATH);
|
||||
|
||||
for (String p : path.split(":"))
|
||||
{
|
||||
File file = new File(p, binaryName);
|
||||
|
||||
if (file.exists())
|
||||
{
|
||||
binary = file.getAbsolutePath();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("resolve {} path to {}", binaryName, binary);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.error("could not resolve binary path", ex);
|
||||
}
|
||||
|
||||
return binary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param config
|
||||
* @param file
|
||||
*/
|
||||
private void useHomebrewInstallation(HgConfig config, File file)
|
||||
{
|
||||
File parent = file.getParentFile().getParentFile();
|
||||
File libDirectory = new File(parent, "lib");
|
||||
|
||||
if (!libDirectory.exists())
|
||||
{
|
||||
libDirectory = new File(parent, "libexec");
|
||||
}
|
||||
|
||||
if (libDirectory.exists())
|
||||
{
|
||||
File pythonDirectory = findPythonDirectory(libDirectory);
|
||||
|
||||
if (pythonDirectory != null)
|
||||
{
|
||||
File sitePackageDirectory = new File(pythonDirectory, "site-packages");
|
||||
|
||||
if (sitePackageDirectory.exists())
|
||||
{
|
||||
libDirectory = sitePackageDirectory;
|
||||
}
|
||||
|
||||
String pythonPath = libDirectory.getPath();
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
{
|
||||
logger.info("found mercurial brew install set python path to {}",
|
||||
pythonPath);
|
||||
}
|
||||
|
||||
config.setPythonPath(pythonPath);
|
||||
}
|
||||
}
|
||||
|
||||
String python = config.getPythonBinary();
|
||||
|
||||
if (PATH_PYTHON.equals(python))
|
||||
{
|
||||
config.setPythonBinary(resolvePath(python));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.installer;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.util.IOUtil;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @deprecated use {@link sonia.scm.autoconfig.AutoConfigurator}
|
||||
*/
|
||||
@Deprecated
|
||||
public class UnixHgInstaller extends AbstractHgInstaller
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
public static final String COMMAND_HG = "hg";
|
||||
|
||||
/** Field description */
|
||||
public static final String COMMAND_PYTHON = "python";
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param baseDirectory
|
||||
* @param config
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void install(File baseDirectory, HgConfig config) throws IOException
|
||||
{
|
||||
// search mercurial (hg)
|
||||
if (Util.isEmpty(config.getHgBinary()))
|
||||
{
|
||||
String hg = IOUtil.search(COMMAND_HG);
|
||||
|
||||
if (Util.isNotEmpty(hg))
|
||||
{
|
||||
config.setHgBinary(hg);
|
||||
|
||||
// search python in the same folder
|
||||
File hgFile = new File(hg);
|
||||
|
||||
if (hgFile.exists())
|
||||
{
|
||||
File pythonFile = new File(hgFile.getParentFile(), COMMAND_PYTHON);
|
||||
|
||||
if (pythonFile.exists())
|
||||
{
|
||||
config.setPythonBinary(pythonFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search python
|
||||
if (Util.isEmpty(config.getPythonBinary()))
|
||||
{
|
||||
config.setPythonBinary(IOUtil.search(COMMAND_PYTHON));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param baseDirectory
|
||||
* @param config
|
||||
*/
|
||||
@Override
|
||||
public void update(File baseDirectory, HgConfig config)
|
||||
{
|
||||
|
||||
// do nothing
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<String> getHgInstallations()
|
||||
{
|
||||
return IOUtil.searchAll(COMMAND_HG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPythonInstallations()
|
||||
{
|
||||
return IOUtil.searchAll(COMMAND_PYTHON);
|
||||
}
|
||||
}
|
||||
@@ -1,418 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.installer;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.util.IOUtil;
|
||||
import sonia.scm.util.RegistryUtil;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class WindowsHgInstaller extends AbstractHgInstaller
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final String FILE_LIBRARY_ZIP = "library.zip";
|
||||
|
||||
/** Field description */
|
||||
private static final String FILE_LIB_MERCURIAL =
|
||||
"Lib\\site-packages\\mercurial";
|
||||
|
||||
/** Field description */
|
||||
private static final String FILE_MERCURIAL_EXE = "hg.exe";
|
||||
|
||||
/** Field description */
|
||||
private static final String FILE_MERCURIAL_SCRIPT = "hg.bat";
|
||||
|
||||
/** Field description */
|
||||
private static final String FILE_SCRIPTS = "Scripts";
|
||||
|
||||
/** Field description */
|
||||
private static final String FILE_TEMPLATES = "templates";
|
||||
|
||||
/** Field description */
|
||||
private static final String[] REGISTRY_HG = new String[]
|
||||
{
|
||||
|
||||
// TortoiseHg
|
||||
"HKEY_CURRENT_USER\\Software\\TortoiseHg",
|
||||
|
||||
// Mercurial
|
||||
"HKEY_CURRENT_USER\\Software\\Mercurial\\InstallDir"
|
||||
};
|
||||
|
||||
/** Field description */
|
||||
private static final String[] REGISTRY_PYTHON = new String[]
|
||||
{
|
||||
|
||||
// .py files
|
||||
"HKEY_CLASSES_ROOT\\Python.File\\shell\\open\\command"
|
||||
};
|
||||
|
||||
/** the logger for WindowsHgInstaller */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(WindowsHgInstaller.class);
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param baseDirectory
|
||||
* @param config
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void install(File baseDirectory, HgConfig config) throws IOException
|
||||
{
|
||||
if (Util.isEmpty(config.getPythonBinary()))
|
||||
{
|
||||
String pythonBinary = getPythonBinary();
|
||||
|
||||
config.setPythonBinary(pythonBinary);
|
||||
}
|
||||
|
||||
if (Util.isEmpty(config.getHgBinary()))
|
||||
{
|
||||
File hgScript = getMercurialScript(config.getPythonBinary());
|
||||
|
||||
if (hgScript != null)
|
||||
{
|
||||
config.setHgBinary(hgScript.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
File hgDirectory = getMercurialDirectory(config.getHgBinary());
|
||||
|
||||
if (hgDirectory != null)
|
||||
{
|
||||
installHg(baseDirectory, config, hgDirectory);
|
||||
}
|
||||
|
||||
checkForOptimizedByteCode(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param baseDirectory
|
||||
* @param config
|
||||
*/
|
||||
@Override
|
||||
public void update(File baseDirectory, HgConfig config) {}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<String> getHgInstallations()
|
||||
{
|
||||
return getInstallations(REGISTRY_HG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPythonInstallations()
|
||||
{
|
||||
return getInstallations(REGISTRY_PYTHON);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param config
|
||||
*/
|
||||
private void checkForOptimizedByteCode(HgConfig config)
|
||||
{
|
||||
boolean optimized = false;
|
||||
String path = config.getPythonPath();
|
||||
|
||||
if (Util.isNotEmpty(path))
|
||||
{
|
||||
for (String part : path.split(";"))
|
||||
{
|
||||
if (checkForOptimizedByteCode(part))
|
||||
{
|
||||
optimized = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.setUseOptimizedBytecode(optimized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param part
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private boolean checkForOptimizedByteCode(String part)
|
||||
{
|
||||
File libDir = new File(part);
|
||||
String[] pyoFiles = libDir.list(new FilenameFilter()
|
||||
{
|
||||
@Override
|
||||
public boolean accept(File file, String name)
|
||||
{
|
||||
return name.toLowerCase().endsWith(".pyo");
|
||||
}
|
||||
});
|
||||
|
||||
return Util.isNotEmpty(pyoFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param baseDirectory
|
||||
* @param config
|
||||
* @param hgDirectory
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void installHg(File baseDirectory, HgConfig config, File hgDirectory)
|
||||
throws IOException
|
||||
{
|
||||
if (logger.isInfoEnabled())
|
||||
{
|
||||
logger.info("installing mercurial {}", hgDirectory.getAbsolutePath());
|
||||
}
|
||||
|
||||
File libDir = new File(baseDirectory, "lib\\hg");
|
||||
|
||||
IOUtil.mkdirs(libDir);
|
||||
|
||||
File libraryZip = new File(hgDirectory, FILE_LIBRARY_ZIP);
|
||||
|
||||
if (libraryZip.exists())
|
||||
{
|
||||
IOUtil.extract(libraryZip, libDir);
|
||||
config.setPythonPath(libDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
File templateDirectory = new File(hgDirectory, FILE_TEMPLATES);
|
||||
|
||||
if (templateDirectory.exists())
|
||||
{
|
||||
IOUtil.copy(templateDirectory, new File(libDir, FILE_TEMPLATES));
|
||||
}
|
||||
|
||||
File hg = new File(hgDirectory, FILE_MERCURIAL_EXE);
|
||||
|
||||
if (hg.exists())
|
||||
{
|
||||
config.setHgBinary(hg.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param registryKeys
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private List<String> getInstallations(String[] registryKeys)
|
||||
{
|
||||
List<String> installations = new ArrayList<String>();
|
||||
|
||||
for (String registryKey : registryKeys)
|
||||
{
|
||||
String path = RegistryUtil.getRegistryValue(registryKey);
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
File file = new File(path, FILE_MERCURIAL_EXE);
|
||||
|
||||
if (file.exists())
|
||||
{
|
||||
installations.add(file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return installations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param hgBinary
|
||||
* @return
|
||||
*/
|
||||
private File getMercurialDirectory(String hgBinary)
|
||||
{
|
||||
File directory = null;
|
||||
|
||||
if (Util.isNotEmpty(hgBinary))
|
||||
{
|
||||
File hg = new File(hgBinary);
|
||||
|
||||
if (hg.exists() && hg.isFile())
|
||||
{
|
||||
directory = hg.getParentFile();
|
||||
}
|
||||
}
|
||||
|
||||
if (directory == null)
|
||||
{
|
||||
directory = getMercurialDirectoryFromRegistry();
|
||||
}
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private File getMercurialDirectoryFromRegistry()
|
||||
{
|
||||
File directory = null;
|
||||
|
||||
for (String registryKey : REGISTRY_HG)
|
||||
{
|
||||
String path = RegistryUtil.getRegistryValue(registryKey);
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
directory = new File(path);
|
||||
|
||||
if (!directory.exists())
|
||||
{
|
||||
directory = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location of the script to run Mercurial, if Mercurial is
|
||||
* installed as a Python package from source. Only packages that include a
|
||||
* templates directory will be recognized.
|
||||
*
|
||||
* @param pythonBinary
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private File getMercurialScript(String pythonBinary)
|
||||
{
|
||||
File hgScript = null;
|
||||
|
||||
if (pythonBinary != null)
|
||||
{
|
||||
File pythonBinaryFile = new File(pythonBinary);
|
||||
|
||||
if (pythonBinaryFile.exists())
|
||||
{
|
||||
File pythonDir = pythonBinaryFile.getParentFile();
|
||||
File scriptsDir = new File(pythonDir, FILE_SCRIPTS);
|
||||
File potentialHgScript = new File(scriptsDir, FILE_MERCURIAL_SCRIPT);
|
||||
File mercurialPackageDir = new File(pythonDir, FILE_LIB_MERCURIAL);
|
||||
File templatesDir = new File(mercurialPackageDir, FILE_TEMPLATES);
|
||||
|
||||
if (potentialHgScript.exists() && templatesDir.exists())
|
||||
{
|
||||
hgScript = potentialHgScript;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hgScript;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String getPythonBinary()
|
||||
{
|
||||
String python = RegistryUtil.getRegistryValue(REGISTRY_PYTHON[0]);
|
||||
|
||||
if (python == null)
|
||||
{
|
||||
python = IOUtil.search(new String[0], "python");
|
||||
}
|
||||
|
||||
return python;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@ import sonia.scm.security.AccessToken;
|
||||
import sonia.scm.security.AccessTokenBuilderFactory;
|
||||
import sonia.scm.security.CipherUtil;
|
||||
import sonia.scm.security.Xsrf;
|
||||
import sonia.scm.web.HgUtil;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -45,8 +44,6 @@ import java.util.Map;
|
||||
@Singleton
|
||||
public class DefaultHgEnvironmentBuilder implements HgEnvironmentBuilder {
|
||||
|
||||
@VisibleForTesting
|
||||
static final String ENV_PYTHON_PATH = "PYTHONPATH";
|
||||
@VisibleForTesting
|
||||
static final String ENV_HOOK_PORT = "SCM_HOOK_PORT";
|
||||
@VisibleForTesting
|
||||
@@ -100,7 +97,6 @@ public class DefaultHgEnvironmentBuilder implements HgEnvironmentBuilder {
|
||||
|
||||
private void read(ImmutableMap.Builder<String, String> env, Repository repository) {
|
||||
HgConfig config = repositoryHandler.getConfig();
|
||||
env.put(ENV_PYTHON_PATH, HgUtil.getPythonPath(config));
|
||||
|
||||
File directory = repositoryHandler.getDirectory(repository.getId());
|
||||
|
||||
|
||||
@@ -69,28 +69,6 @@ public class HgConfig extends RepositoryConfig {
|
||||
return hgBinary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getPythonBinary()
|
||||
{
|
||||
return pythonBinary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getPythonPath()
|
||||
{
|
||||
return pythonPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
@@ -102,17 +80,6 @@ public class HgConfig extends RepositoryConfig {
|
||||
return showRevisionInId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isUseOptimizedBytecode()
|
||||
{
|
||||
return useOptimizedBytecode;
|
||||
}
|
||||
|
||||
public boolean isEnableHttpPostArgs() {
|
||||
return enableHttpPostArgs;
|
||||
}
|
||||
@@ -126,8 +93,7 @@ public class HgConfig extends RepositoryConfig {
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return super.isValid() && Util.isNotEmpty(hgBinary)
|
||||
&& Util.isNotEmpty(pythonBinary);
|
||||
return super.isValid() && Util.isNotEmpty(hgBinary);
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
@@ -154,28 +120,6 @@ public class HgConfig extends RepositoryConfig {
|
||||
this.hgBinary = hgBinary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param pythonBinary
|
||||
*/
|
||||
public void setPythonBinary(String pythonBinary)
|
||||
{
|
||||
this.pythonBinary = pythonBinary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param pythonPath
|
||||
*/
|
||||
public void setPythonPath(String pythonPath)
|
||||
{
|
||||
this.pythonPath = pythonPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
@@ -191,17 +135,6 @@ public class HgConfig extends RepositoryConfig {
|
||||
this.enableHttpPostArgs = enableHttpPostArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param useOptimizedBytecode
|
||||
*/
|
||||
public void setUseOptimizedBytecode(boolean useOptimizedBytecode)
|
||||
{
|
||||
this.useOptimizedBytecode = useOptimizedBytecode;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
@@ -210,15 +143,6 @@ public class HgConfig extends RepositoryConfig {
|
||||
/** Field description */
|
||||
private String hgBinary;
|
||||
|
||||
/** Field description */
|
||||
private String pythonBinary;
|
||||
|
||||
/** Field description */
|
||||
private String pythonPath = "";
|
||||
|
||||
/** Field description */
|
||||
private boolean useOptimizedBytecode = false;
|
||||
|
||||
/** Field description */
|
||||
private boolean showRevisionInId = false;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ package sonia.scm.repository;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
@@ -36,16 +37,19 @@ import java.io.File;
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public enum HgPythonScript {
|
||||
public enum HgExtensions {
|
||||
|
||||
HOOK("scmhooks.py"), HGWEB("hgweb.py");
|
||||
HOOK("scmhooks.py"),
|
||||
CGISERVE("cgiserve.py"),
|
||||
VERSION("scmversion.py"),
|
||||
FILEVIEW("fileview.py");
|
||||
|
||||
private static final String BASE_DIRECTORY = "lib".concat(File.separator).concat("python");
|
||||
private static final String BASE_RESOURCE = "/sonia/scm/python/";
|
||||
|
||||
private final String name;
|
||||
|
||||
HgPythonScript(String name) {
|
||||
HgExtensions(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@@ -53,6 +57,10 @@ public enum HgPythonScript {
|
||||
return new File(context.getBaseDirectory(), BASE_DIRECTORY);
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return getFile(SCMContext.getContext());
|
||||
}
|
||||
|
||||
public File getFile(SCMContextProvider context) {
|
||||
return new File(getScriptDirectory(context), name);
|
||||
}
|
||||
@@ -30,10 +30,9 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.autoconfig.AutoConfigurator;
|
||||
import sonia.scm.installer.HgInstaller;
|
||||
import sonia.scm.installer.HgInstallerFactory;
|
||||
import sonia.scm.io.ExtendedCommand;
|
||||
import sonia.scm.io.INIConfiguration;
|
||||
import sonia.scm.io.INIConfigurationWriter;
|
||||
@@ -45,7 +44,6 @@ import sonia.scm.repository.spi.HgVersionCommand;
|
||||
import sonia.scm.repository.spi.HgWorkingCopyFactory;
|
||||
import sonia.scm.store.ConfigurationStoreFactory;
|
||||
import sonia.scm.util.IOUtil;
|
||||
import sonia.scm.util.SystemUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -53,15 +51,12 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Optional;
|
||||
|
||||
@Singleton
|
||||
@Extension
|
||||
public class HgRepositoryHandler
|
||||
extends AbstractSimpleRepositoryHandler<HgConfig> {
|
||||
|
||||
public static final String RESOURCE_VERSION = "sonia/scm/version/scm-hg-plugin";
|
||||
public static final String TYPE_DISPLAYNAME = "Mercurial";
|
||||
public static final String TYPE_NAME = "hg";
|
||||
public static final RepositoryType TYPE = new RepositoryType(
|
||||
@@ -78,43 +73,25 @@ public class HgRepositoryHandler
|
||||
private static final String CONFIG_KEY_REPOSITORY_ID = "repositoryid";
|
||||
|
||||
private final HgWorkingCopyFactory workingCopyFactory;
|
||||
private final AutoConfigurator configurator;
|
||||
|
||||
@Inject
|
||||
public HgRepositoryHandler(ConfigurationStoreFactory storeFactory,
|
||||
RepositoryLocationResolver repositoryLocationResolver,
|
||||
PluginLoader pluginLoader, HgWorkingCopyFactory workingCopyFactory) {
|
||||
PluginLoader pluginLoader, HgWorkingCopyFactory workingCopyFactory, AutoConfigurator configurator) {
|
||||
super(storeFactory, repositoryLocationResolver, pluginLoader);
|
||||
this.workingCopyFactory = workingCopyFactory;
|
||||
this.configurator = configurator;
|
||||
}
|
||||
|
||||
public void doAutoConfiguration(HgConfig autoConfig) {
|
||||
HgInstaller installer = HgInstallerFactory.createInstaller();
|
||||
|
||||
try {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("installing mercurial with {}", installer.getClass().getName());
|
||||
}
|
||||
|
||||
installer.install(baseDirectory, autoConfig);
|
||||
config = autoConfig;
|
||||
storeConfig();
|
||||
} catch (IOException ioe) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Could not write Hg CGI for inital config. "
|
||||
+ "HgWeb may not function until a new Hg config is set", ioe);
|
||||
}
|
||||
}
|
||||
configurator.configure(autoConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(SCMContextProvider context) {
|
||||
super.init(context);
|
||||
writePythonScripts(context);
|
||||
|
||||
// fix wrong hg.bat from package installation
|
||||
if (SystemUtil.isWindows()) {
|
||||
HgWindowsPackageFix.fixHgPackage(context, getConfig());
|
||||
}
|
||||
writeHgExtensions(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -122,20 +99,18 @@ public class HgRepositoryHandler
|
||||
super.loadConfig();
|
||||
|
||||
if (config == null) {
|
||||
HgConfig config = null;
|
||||
Optional<AutoConfigurator> autoConfigurator = AutoConfigurator.get();
|
||||
if (autoConfigurator.isPresent()) {
|
||||
config = autoConfigurator.get().configure();
|
||||
}
|
||||
|
||||
if (config != null && config.isValid()) {
|
||||
this.config = config;
|
||||
storeConfig();
|
||||
} else {
|
||||
// do the old configuration
|
||||
doAutoConfiguration(config != null ? config : new HgConfig());
|
||||
}
|
||||
config = new HgConfig();
|
||||
storeConfig();
|
||||
}
|
||||
|
||||
if (!isConfigValid(config)) {
|
||||
doAutoConfiguration(config);
|
||||
storeConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isConfigValid(HgConfig config) {
|
||||
return config.isValid() && new HgVerifier().isValid(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -154,10 +129,10 @@ public class HgRepositoryHandler
|
||||
}
|
||||
|
||||
String getVersionInformation(HgVersionCommand command) {
|
||||
String version = getStringFromResource(RESOURCE_VERSION, DEFAULT_VERSION_INFORMATION);
|
||||
HgVersion hgVersion = command.get();
|
||||
logger.debug("mercurial/python informations: {}", hgVersion);
|
||||
return MessageFormat.format(version, hgVersion.getPython(), hgVersion.getMercurial());
|
||||
return String.format("scm-hg-version/%s %s",
|
||||
SCMContext.getContext().getVersion(),
|
||||
command.get()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -176,8 +151,7 @@ public class HgRepositoryHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes .hg/hgrc, disables hg access control and added scm hook support.
|
||||
* see HgPermissionFilter
|
||||
* Writes repository to .hg/hgrc.
|
||||
*
|
||||
* @param repository
|
||||
* @param directory
|
||||
@@ -205,10 +179,10 @@ public class HgRepositoryHandler
|
||||
return HgConfig.class;
|
||||
}
|
||||
|
||||
private void writePythonScripts(SCMContextProvider context) {
|
||||
IOUtil.mkdirs(HgPythonScript.getScriptDirectory(context));
|
||||
private void writeHgExtensions(SCMContextProvider context) {
|
||||
IOUtil.mkdirs(HgExtensions.getScriptDirectory(context));
|
||||
|
||||
for (HgPythonScript script : HgPythonScript.values()) {
|
||||
for (HgExtensions script : HgExtensions.values()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("write python script {}", script.getName());
|
||||
}
|
||||
@@ -216,16 +190,16 @@ public class HgRepositoryHandler
|
||||
try (InputStream content = input(script); OutputStream output = output(context, script)) {
|
||||
IOUtil.copy(content, output);
|
||||
} catch (IOException ex) {
|
||||
logger.error("could not write script", ex);
|
||||
throw new IllegalStateException("could not write hg extension", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream input(HgPythonScript script) {
|
||||
private InputStream input(HgExtensions script) {
|
||||
return HgRepositoryHandler.class.getResourceAsStream(script.getResourcePath());
|
||||
}
|
||||
|
||||
private OutputStream output(SCMContextProvider context, HgPythonScript script) throws FileNotFoundException {
|
||||
private OutputStream output(SCMContextProvider context, HgExtensions script) throws FileNotFoundException {
|
||||
return new FileOutputStream(script.getFile(context));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.io.SimpleCommand;
|
||||
import sonia.scm.io.SimpleCommandResult;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class HgVerifier {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HgVerifier.class);
|
||||
private final VersionResolver versionResolver;
|
||||
|
||||
public HgVerifier() {
|
||||
this.versionResolver = defaultVersionResolver();
|
||||
}
|
||||
|
||||
HgVerifier(VersionResolver versionResolver) {
|
||||
this.versionResolver = versionResolver;
|
||||
}
|
||||
|
||||
public boolean isValid(HgConfig config) {
|
||||
return isValid(config.getHgBinary());
|
||||
}
|
||||
|
||||
public boolean isValid(String hg) {
|
||||
return isValid(Paths.get(hg));
|
||||
}
|
||||
|
||||
public boolean isValid(Path hg) {
|
||||
LOG.trace("check if hg binary {} is valid", hg);
|
||||
if (!Files.isRegularFile(hg)) {
|
||||
LOG.warn("{} is not a regular file", hg);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Files.isExecutable(hg)) {
|
||||
LOG.warn("{} is not executable", hg);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
String version = versionResolver.resolveVersion(hg);
|
||||
return isVersionValid(hg, version);
|
||||
} catch (IOException ex) {
|
||||
LOG.warn("failed to resolve version of {}: ", hg, ex);
|
||||
return false;
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
LOG.warn("failed to resolve version of {}: ", hg, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isVersionValid(Path hg, String version) {
|
||||
String[] parts = version.split("\\.");
|
||||
if (parts.length < 2) {
|
||||
LOG.warn("{} returned invalid version: {}", hg, version);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
int major = Integer.parseInt(parts[0]);
|
||||
if (major < 4) {
|
||||
LOG.warn("{} is too old, we need at least mercurial 4.x", hg);
|
||||
return false;
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
LOG.warn("{} returned invalid version {}", hg, version);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private VersionResolver defaultVersionResolver() {
|
||||
return hg -> {
|
||||
SimpleCommand command = new SimpleCommand(hg.toString(), "version", "--template", "{ver}");
|
||||
SimpleCommandResult result = command.execute();
|
||||
if (!result.isSuccessfull()) {
|
||||
throw new IOException("failed to get version from hg");
|
||||
}
|
||||
return result.getOutput().trim();
|
||||
};
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface VersionResolver {
|
||||
String resolveVersion(Path hg) throws IOException, InterruptedException;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.util.IOUtil;
|
||||
import sonia.scm.web.HgUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public final class HgWindowsPackageFix
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
static final String MODIFY_MARK_01 = ".setbinary";
|
||||
|
||||
/** Field description */
|
||||
static final String MODIFY_MARK_02 = ".setpythonpath";
|
||||
|
||||
/** Field description */
|
||||
private static final String HG_BAT = "hg.bat";
|
||||
|
||||
/** Field description */
|
||||
private static final String HG_PY = "hg.py";
|
||||
|
||||
/** Field description */
|
||||
private static final String PYTHONPATH_FIXED =
|
||||
"set PYTHONPATH=%~dp0..\\lib;%PYTHONHOME%\\Lib;%PYTHONPATH%";
|
||||
|
||||
/** Field description */
|
||||
private static final String PYTHONPATH_WRONG =
|
||||
"set PYTHONPATH=%~dp0..\\lib;%PYTHONHOME%\\Lib";
|
||||
|
||||
/**
|
||||
* the logger for HgUtil
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(HgUtil.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
private HgWindowsPackageFix() {}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* @param config
|
||||
*/
|
||||
public static void fixHgPackage(SCMContextProvider context, HgConfig config)
|
||||
{
|
||||
if ((config != null) && config.isValid())
|
||||
{
|
||||
String basePath = context.getBaseDirectory().getAbsolutePath();
|
||||
|
||||
String hg = config.getHgBinary();
|
||||
|
||||
if (hg.startsWith(basePath) && hg.endsWith(HG_BAT))
|
||||
{
|
||||
File file = new File(hg);
|
||||
|
||||
fixHgBat(file);
|
||||
|
||||
file = new File(file.getParentFile(), HG_PY);
|
||||
fixHgPy(file);
|
||||
}
|
||||
}
|
||||
else if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(
|
||||
"could not fix hg.py, because the configuration is not valid");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visible for testing
|
||||
*
|
||||
* @param hgBat
|
||||
*/
|
||||
static void fixHgBat(File hgBat)
|
||||
{
|
||||
if (hgBat.exists())
|
||||
{
|
||||
File binDirectory = hgBat.getParentFile();
|
||||
File modifyMark = new File(binDirectory, MODIFY_MARK_02);
|
||||
|
||||
if (!modifyMark.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
String content = Files.toString(hgBat, Charsets.UTF_8);
|
||||
|
||||
if (!content.contains(PYTHONPATH_FIXED))
|
||||
{
|
||||
content = content.replace(PYTHONPATH_WRONG, PYTHONPATH_FIXED);
|
||||
Files.write(content, hgBat, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
createModifyMark(modifyMark);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.error("could not read content of {}", hgBat);
|
||||
|
||||
throw Throwables.propagate(ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.debug("hg.bat allready fixed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.warn("could not find hg.bat at {}", hgBat);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visible for testing
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param hgPy
|
||||
*/
|
||||
static void fixHgPy(File hgPy)
|
||||
{
|
||||
|
||||
if (hgPy.exists())
|
||||
{
|
||||
|
||||
File binDirectory = hgPy.getParentFile();
|
||||
File modifyMark = new File(binDirectory, MODIFY_MARK_01);
|
||||
|
||||
if (!modifyMark.exists())
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("check hg.py for setbinary at {}", hgPy);
|
||||
}
|
||||
|
||||
if (!isSetBinaryAvailable(hgPy))
|
||||
{
|
||||
injectSetBinary(hgPy);
|
||||
}
|
||||
else
|
||||
{
|
||||
createModifyMark(modifyMark);
|
||||
}
|
||||
}
|
||||
else if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("hg.py allready fixed");
|
||||
}
|
||||
|
||||
}
|
||||
else if (logger.isWarnEnabled())
|
||||
{
|
||||
logger.warn("could not find hg.py at {}", hgPy);
|
||||
}
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Visible for testing
|
||||
*
|
||||
*
|
||||
* @param hg
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static boolean isSetBinaryAvailable(File hg)
|
||||
{
|
||||
boolean setBinaryAvailable = false;
|
||||
|
||||
BufferedReader reader = null;
|
||||
|
||||
try
|
||||
{
|
||||
reader = Files.newReader(hg, Charsets.UTF_8);
|
||||
|
||||
String line = reader.readLine();
|
||||
|
||||
while (line != null)
|
||||
{
|
||||
line = line.trim();
|
||||
|
||||
if (line.contains("mercurial.util.setbinary") &&!line.startsWith("#"))
|
||||
{
|
||||
setBinaryAvailable = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
line = reader.readLine();
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.error("could not check hg.bat", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(reader);
|
||||
}
|
||||
|
||||
return setBinaryAvailable;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param modifyMark
|
||||
*/
|
||||
private static void createModifyMark(File modifyMark)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!modifyMark.createNewFile())
|
||||
{
|
||||
throw new RuntimeException("could not create modify mark");
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new RuntimeException("could not create modify mark", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param hg
|
||||
*/
|
||||
private static void injectSetBinary(File hg)
|
||||
{
|
||||
String lineSeparator = System.getProperty("line.separator");
|
||||
File mod = new File(hg.getParentFile(), MODIFY_MARK_01);
|
||||
|
||||
hg.renameTo(mod);
|
||||
|
||||
BufferedWriter writer = null;
|
||||
BufferedReader reader = null;
|
||||
|
||||
try
|
||||
{
|
||||
writer = Files.newWriter(hg, Charsets.UTF_8);
|
||||
reader = Files.newReader(mod, Charsets.UTF_8);
|
||||
|
||||
String line = reader.readLine();
|
||||
|
||||
while (line != null)
|
||||
{
|
||||
|
||||
if (line.trim().equals("mercurial.dispatch.run()"))
|
||||
{
|
||||
writer.write("for fp in (sys.stdin, sys.stdout, sys.stderr):");
|
||||
writer.write(lineSeparator);
|
||||
writer.write(" mercurial.util.setbinary(fp)");
|
||||
writer.write(lineSeparator);
|
||||
writer.write(lineSeparator);
|
||||
}
|
||||
|
||||
writer.write(line);
|
||||
writer.write(lineSeparator);
|
||||
line = reader.readLine();
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.error("could not check hg.bat", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(reader);
|
||||
IOUtil.close(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,93 +24,71 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.HgVersion;
|
||||
import sonia.scm.repository.HgExtensions;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class HgVersionCommand {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HgVersionCommand.class);
|
||||
|
||||
@VisibleForTesting
|
||||
static final String[] HG_ARGS = {
|
||||
"version", "--template", "{ver}"
|
||||
};
|
||||
|
||||
@VisibleForTesting
|
||||
static final String[] PYTHON_ARGS = {
|
||||
"-c", "import sys; print(sys.version)"
|
||||
};
|
||||
public static final String UNKNOWN = "python/x.y.z mercurial/x.y.z";
|
||||
|
||||
private final HgConfig config;
|
||||
private final String extension;
|
||||
private final ProcessExecutor executor;
|
||||
|
||||
public HgVersionCommand(HgConfig config) {
|
||||
this(config, command -> new ProcessBuilder(command).start());
|
||||
this(config, extension(), command -> new ProcessBuilder(command).start());
|
||||
}
|
||||
|
||||
HgVersionCommand(HgConfig config, ProcessExecutor executor) {
|
||||
HgVersionCommand(HgConfig config, String extension, ProcessExecutor executor) {
|
||||
this.config = config;
|
||||
this.extension = extension;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public HgVersion get() {
|
||||
return new HgVersion(getHgVersion(), getPythonVersion());
|
||||
private static String extension() {
|
||||
return HgExtensions.VERSION.getFile(SCMContext.getContext()).getAbsolutePath();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private String getPythonVersion() {
|
||||
public String get() {
|
||||
List<String> command = createCommand();
|
||||
try {
|
||||
String content = exec(config.getPythonBinary(), PYTHON_ARGS);
|
||||
int index = content.indexOf(' ');
|
||||
if (index > 0) {
|
||||
return content.substring(0, index);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
LOG.warn("failed to get python version", ex);
|
||||
} catch (InterruptedException ex) {
|
||||
LOG.warn("failed to get python version", ex);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return HgVersion.UNKNOWN;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private String getHgVersion() {
|
||||
try {
|
||||
return exec(config.getHgBinary(), HG_ARGS).trim();
|
||||
return exec(command).trim();
|
||||
} catch (IOException ex) {
|
||||
LOG.warn("failed to get mercurial version", ex);
|
||||
} catch (InterruptedException ex) {
|
||||
LOG.warn("failed to get mercurial version", ex);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return HgVersion.UNKNOWN;
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
private String exec(String command, String[] args) throws IOException, InterruptedException {
|
||||
List<String> cmd = new ArrayList<>();
|
||||
cmd.add(command);
|
||||
cmd.addAll(Arrays.asList(args));
|
||||
private List<String> createCommand() {
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add(config.getHgBinary());
|
||||
command.add("--config");
|
||||
command.add("extensions.scmversion=" + extension);
|
||||
command.add("scmversion");
|
||||
return command;
|
||||
}
|
||||
|
||||
Process process = executor.execute(cmd);
|
||||
private String exec(List<String> command) throws IOException, InterruptedException {
|
||||
Process process = executor.execute(command);
|
||||
byte[] bytes = ByteStreams.toByteArray(process.getInputStream());
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode != 0) {
|
||||
throw new IOException("process ends with exit code " + exitCode);
|
||||
}
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
return new String(bytes, StandardCharsets.UTF_8).trim();
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.aragost.javahg.commands.PullCommand;
|
||||
import com.aragost.javahg.commands.StatusCommand;
|
||||
import com.aragost.javahg.commands.UpdateCommand;
|
||||
import com.aragost.javahg.commands.flags.CloneCommandFlags;
|
||||
import sonia.scm.repository.HgExtensions;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.work.SimpleWorkingCopyFactory;
|
||||
import sonia.scm.repository.work.WorkingCopyPool;
|
||||
@@ -99,7 +100,8 @@ public class SimpleHgWorkingCopyFactory extends SimpleWorkingCopyFactory<Reposit
|
||||
|
||||
@Override
|
||||
public void configure(PullCommand pullCommand) {
|
||||
pullCommand.cmdAppend("--config", "hooks.changegroup.scm=python:scmhooks.post_hook");
|
||||
pullCommand.cmdAppend("--config", "hooks.pretxnchangegroup.scm=python:scmhooks.pre_hook");
|
||||
String hooks = HgExtensions.HOOK.getFile().getAbsolutePath();
|
||||
pullCommand.cmdAppend("--config", String.format("hooks.pretxnchangegroup.scm=python:%s:pre_hook", hooks));
|
||||
pullCommand.cmdAppend("--config", String.format("hooks.changegroup.scm=python:%s:post_hook", hooks));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,51 +21,29 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository.spi.javahg;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.aragost.javahg.MercurialExtension;
|
||||
import com.aragost.javahg.internals.Utils;
|
||||
import sonia.scm.repository.HgExtensions;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class HgFileviewExtension extends MercurialExtension
|
||||
{
|
||||
public class HgFileviewExtension extends MercurialExtension {
|
||||
|
||||
/** Field description */
|
||||
static final String NAME = "fileview";
|
||||
|
||||
/** Field description */
|
||||
private static final String PATH =
|
||||
Utils.resourceAsFile("/sonia/scm/hg/ext/fileview.py").getAbsolutePath();
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getPath()
|
||||
{
|
||||
return PATH;
|
||||
public String getPath() {
|
||||
return HgExtensions.FILEVIEW.getFile().getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,14 +33,15 @@ import sonia.scm.SCMContext;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.HgEnvironmentBuilder;
|
||||
import sonia.scm.repository.HgPythonScript;
|
||||
import sonia.scm.repository.HgExtensions;
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryRequestListenerUtil;
|
||||
import sonia.scm.repository.spi.ScmProviderHttpServlet;
|
||||
import sonia.scm.util.AssertUtil;
|
||||
import sonia.scm.web.cgi.CGIExecutor;
|
||||
import sonia.scm.web.cgi.CGIExecutorFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import sonia.scm.web.cgi.EnvList;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
@@ -49,8 +50,8 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -81,7 +82,7 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet
|
||||
this.requestListenerUtil = requestListenerUtil;
|
||||
this.environmentBuilder = environmentBuilder;
|
||||
this.exceptionHandler = new HgCGIExceptionHandler();
|
||||
this.command = HgPythonScript.HGWEB.getFile(SCMContext.getContext());
|
||||
this.extension = HgExtensions.CGISERVE.getFile(SCMContext.getContext());
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
@@ -108,17 +109,6 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @param repository
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws ServletException
|
||||
*/
|
||||
private void handleRequest(HttpServletRequest request,
|
||||
HttpServletResponse response, Repository repository)
|
||||
throws ServletException, IOException
|
||||
@@ -135,17 +125,6 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @param repository
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws ServletException
|
||||
*/
|
||||
private void process(HttpServletRequest request,
|
||||
HttpServletResponse response, Repository repository)
|
||||
throws IOException, ServletException
|
||||
@@ -162,38 +141,33 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet
|
||||
EnvList env = executor.getEnvironment();
|
||||
environmentBuilder.write(repository).forEach(env::set);
|
||||
|
||||
String interpreter = getInterpreter();
|
||||
File directory = handler.getDirectory(repository.getId());
|
||||
executor.setWorkDirectory(directory);
|
||||
executor.setArgs(createArgs());
|
||||
|
||||
if (interpreter != null)
|
||||
{
|
||||
executor.setInterpreter(interpreter);
|
||||
}
|
||||
|
||||
executor.execute(command.getAbsolutePath());
|
||||
HgConfig config = handler.getConfig();
|
||||
executor.execute(config.getHgBinary());
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
@Nonnull
|
||||
private List<String> createArgs() {
|
||||
List<String> args = new ArrayList<>();
|
||||
config(args, "extensions.cgiserve", extension.getAbsolutePath());
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String getInterpreter()
|
||||
{
|
||||
HgConfig config = handler.getConfig();
|
||||
String hooks = HgExtensions.HOOK.getFile().getAbsolutePath();
|
||||
config(args, "hooks.pretxnchangegroup.scm", String.format("python:%s:pre_hook", hooks));
|
||||
config(args, "hooks.changegroup.scm", String.format("python:%s:post_hook", hooks));
|
||||
|
||||
AssertUtil.assertIsNotNull(config);
|
||||
config(args, "web.push_ssl", "false");
|
||||
config(args, "web.allow_read", "*");
|
||||
config(args, "web.allow_push", "*");
|
||||
args.add("cgiserve");
|
||||
return args;
|
||||
}
|
||||
|
||||
String python = config.getPythonBinary();
|
||||
|
||||
if ((python != null) && config.isUseOptimizedBytecode())
|
||||
{
|
||||
python = python.concat(" -O");
|
||||
}
|
||||
|
||||
return python;
|
||||
private void config(List<String> args, String key, String value) {
|
||||
args.add("--config");
|
||||
args.add(key + "=" + value);
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
@@ -202,7 +176,7 @@ public class HgCGIServlet extends HttpServlet implements ScmProviderHttpServlet
|
||||
private final CGIExecutorFactory cgiExecutorFactory;
|
||||
|
||||
/** Field description */
|
||||
private final File command;
|
||||
private final File extension;
|
||||
|
||||
/** Field description */
|
||||
private final ScmConfiguration configuration;
|
||||
|
||||
@@ -29,10 +29,7 @@ package sonia.scm.web;
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import sonia.scm.api.v2.resources.HgConfigDtoToHgConfigMapper;
|
||||
import sonia.scm.api.v2.resources.HgConfigInstallationsToDtoMapper;
|
||||
import sonia.scm.api.v2.resources.HgConfigPackagesToDtoMapper;
|
||||
import sonia.scm.api.v2.resources.HgConfigToHgConfigDtoMapper;
|
||||
import sonia.scm.installer.HgPackageReader;
|
||||
import sonia.scm.plugin.Extension;
|
||||
import sonia.scm.repository.spi.HgWorkingCopyFactory;
|
||||
import sonia.scm.repository.spi.SimpleHgWorkingCopyFactory;
|
||||
@@ -46,12 +43,8 @@ public class HgServletModule extends ServletModule {
|
||||
|
||||
@Override
|
||||
protected void configureServlets() {
|
||||
bind(HgPackageReader.class);
|
||||
|
||||
bind(HgConfigDtoToHgConfigMapper.class).to(Mappers.getMapper(HgConfigDtoToHgConfigMapper.class).getClass());
|
||||
bind(HgConfigToHgConfigDtoMapper.class).to(Mappers.getMapper(HgConfigToHgConfigDtoMapper.class).getClass());
|
||||
bind(HgConfigPackagesToDtoMapper.class).to(Mappers.getMapper(HgConfigPackagesToDtoMapper.class).getClass());
|
||||
bind(HgConfigInstallationsToDtoMapper.class);
|
||||
|
||||
bind(HgWorkingCopyFactory.class).to(SimpleHgWorkingCopyFactory.class);
|
||||
}
|
||||
|
||||
@@ -24,13 +24,8 @@
|
||||
|
||||
package sonia.scm.web;
|
||||
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.HgPythonScript;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -41,26 +36,6 @@ public final class HgUtil {
|
||||
|
||||
private HgUtil() {}
|
||||
|
||||
public static String getPythonPath(HgConfig config) {
|
||||
String pythonPath = Util.EMPTY_STRING;
|
||||
|
||||
if (config != null) {
|
||||
pythonPath = Util.nonNull(config.getPythonPath());
|
||||
}
|
||||
|
||||
if (Util.isNotEmpty(pythonPath)) {
|
||||
pythonPath = pythonPath.concat(File.pathSeparator);
|
||||
}
|
||||
|
||||
pythonPath = pythonPath.concat(
|
||||
HgPythonScript.getScriptDirectory(
|
||||
SCMContext.getContext()
|
||||
).getAbsolutePath()
|
||||
);
|
||||
|
||||
return pythonPath;
|
||||
}
|
||||
|
||||
public static String getRevision(String revision) {
|
||||
return Util.isEmpty(revision) ? REVISION_TIP : revision;
|
||||
}
|
||||
|
||||
@@ -28,10 +28,7 @@ import { InputField, Checkbox } from "@scm-manager/ui-components";
|
||||
|
||||
type Configuration = {
|
||||
hgBinary: string;
|
||||
pythonBinary: string;
|
||||
pythonPath?: string;
|
||||
encoding: string;
|
||||
useOptimizedBytecode: boolean;
|
||||
showRevisionInId: boolean;
|
||||
enableHttpPostArgs: boolean;
|
||||
_links: Links;
|
||||
@@ -58,7 +55,7 @@ class HgConfigurationForm extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
updateValidationStatus = () => {
|
||||
const requiredFields = ["hgBinary", "pythonBinary", "encoding"];
|
||||
const requiredFields = ["hgBinary", "encoding"];
|
||||
|
||||
const validationErrors = [];
|
||||
for (const field of requiredFields) {
|
||||
@@ -130,11 +127,8 @@ class HgConfigurationForm extends React.Component<Props, State> {
|
||||
return (
|
||||
<div className="columns is-multiline">
|
||||
{this.inputField("hgBinary")}
|
||||
{this.inputField("pythonBinary")}
|
||||
{this.inputField("pythonPath")}
|
||||
{this.inputField("encoding")}
|
||||
<div className="column is-half">
|
||||
{this.checkbox("useOptimizedBytecode")}
|
||||
{this.checkbox("showRevisionInId")}
|
||||
</div>
|
||||
<div className="column is-half">
|
||||
|
||||
@@ -13,14 +13,8 @@
|
||||
"title": "Mercurial Konfiguration",
|
||||
"hgBinary": "HG Binary",
|
||||
"hgBinaryHelpText": "Pfad des Mercurial Binary.",
|
||||
"pythonBinary": "Python Binary",
|
||||
"pythonBinaryHelpText": "Pfad des Python binary.",
|
||||
"pythonPath": "Python Module Such Pfad",
|
||||
"pythonPathHelpText": "Python Module Such Pfad (PYTHONPATH).",
|
||||
"encoding": "Encoding",
|
||||
"encodingHelpText": "Repository Encoding.",
|
||||
"useOptimizedBytecode": "Optimized Bytecode (.pyo)",
|
||||
"useOptimizedBytecodeHelpText": "Verwende den Python '-O' Switch.",
|
||||
"showRevisionInId": "Revision anzeigen",
|
||||
"showRevisionInIdHelpText": "Die Revision als Teil der Node ID anzeigen.",
|
||||
"enableHttpPostArgs": "HttpPostArgs Protocol aktivieren",
|
||||
|
||||
@@ -13,14 +13,8 @@
|
||||
"title": "Mercurial Configuration",
|
||||
"hgBinary": "HG Binary",
|
||||
"hgBinaryHelpText": "Location of Mercurial binary.",
|
||||
"pythonBinary": "Python Binary",
|
||||
"pythonBinaryHelpText": "Location of Python binary.",
|
||||
"pythonPath": "Python Module Search Path",
|
||||
"pythonPathHelpText": "Python Module Search Path (PYTHONPATH).",
|
||||
"encoding": "Encoding",
|
||||
"encodingHelpText": "Repository Encoding.",
|
||||
"useOptimizedBytecode": "Optimized Bytecode (.pyo)",
|
||||
"useOptimizedBytecodeHelpText": "Use the Python '-O' switch.",
|
||||
"showRevisionInId": "Show Revision",
|
||||
"showRevisionInIdHelpText": "Show revision as part of the node id.",
|
||||
"enableHttpPostArgs": "Enable HttpPostArgs Protocol",
|
||||
|
||||
@@ -22,33 +22,20 @@
|
||||
# SOFTWARE.
|
||||
#
|
||||
|
||||
|
||||
import os
|
||||
from mercurial import demandimport, ui as uimod, hg
|
||||
from mercurial import registrar
|
||||
from mercurial.hgweb import hgweb, wsgicgi
|
||||
|
||||
demandimport.enable()
|
||||
cmdtable = {}
|
||||
|
||||
try:
|
||||
u = uimod.ui.load()
|
||||
except AttributeError:
|
||||
# For installations earlier than Mercurial 4.1
|
||||
u = uimod.ui()
|
||||
from mercurial import registrar
|
||||
command = registrar.command(cmdtable)
|
||||
except (AttributeError, ImportError):
|
||||
# Fallback to hg < 4.3 support
|
||||
from mercurial import cmdutil
|
||||
command = cmdutil.command(cmdtable)
|
||||
|
||||
u.setconfig(b'web', b'push_ssl', b'false')
|
||||
u.setconfig(b'web', b'allow_read', b'*')
|
||||
u.setconfig(b'web', b'allow_push', b'*')
|
||||
|
||||
u.setconfig(b'hooks', b'changegroup.scm', b'python:scmhooks.post_hook')
|
||||
u.setconfig(b'hooks', b'pretxnchangegroup.scm', b'python:scmhooks.pre_hook')
|
||||
|
||||
# pass SCM_HTTP_POST_ARGS to enable experimental httppostargs protocol of mercurial
|
||||
# SCM_HTTP_POST_ARGS is set by HgCGIServlet
|
||||
# Issue 970: https://goo.gl/poascp
|
||||
u.setconfig(b'experimental', b'httppostargs', os.environ['SCM_HTTP_POST_ARGS'].encode())
|
||||
|
||||
# open repository
|
||||
# SCM_REPOSITORY_PATH contains the repository path and is set by HgCGIServlet
|
||||
r = hg.repository(u, os.environ['SCM_REPOSITORY_PATH'].encode())
|
||||
application = hgweb(r)
|
||||
wsgicgi.launch(application)
|
||||
@command(b'cgiserve')
|
||||
def cgiserve(ui, repo, **opts):
|
||||
application = hgweb(repo)
|
||||
wsgicgi.launch(application)
|
||||
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
|
||||
from mercurial import util
|
||||
import platform
|
||||
|
||||
cmdtable = {}
|
||||
|
||||
try:
|
||||
from mercurial import registrar
|
||||
command = registrar.command(cmdtable)
|
||||
except (AttributeError, ImportError):
|
||||
# Fallback to hg < 4.3 support
|
||||
from mercurial import cmdutil
|
||||
command = cmdutil.command(cmdtable)
|
||||
|
||||
@command(b'scmversion', norepo=True)
|
||||
def scmversion(ui, **opts):
|
||||
pythonVersion = platform.python_version().encode("utf-8")
|
||||
ui.write(b"python/%s mercurial/%s\n" % (pythonVersion, util.version()))
|
||||
@@ -1 +0,0 @@
|
||||
scm-hg-version/${project.version} python/{0} mercurial/{1}
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.github.sdorra.shiro.ShiroRule;
|
||||
@@ -79,8 +79,8 @@ public class HgConfigAutoConfigurationResourceTest {
|
||||
|
||||
when(resourceProvider.get()).thenReturn(resource);
|
||||
dispatcher.addSingletonResource(
|
||||
new HgConfigResource(null, null, null, null,
|
||||
resourceProvider, null));
|
||||
new HgConfigResource(null, null, null,
|
||||
resourceProvider));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -48,10 +48,7 @@ public class HgConfigDtoToHgConfigMapperTest {
|
||||
|
||||
assertEquals("ABC", config.getEncoding());
|
||||
assertEquals("/etc/hg", config.getHgBinary());
|
||||
assertEquals("/py", config.getPythonBinary());
|
||||
assertEquals("/etc/", config.getPythonPath());
|
||||
assertTrue(config.isShowRevisionInId());
|
||||
assertTrue(config.isUseOptimizedBytecode());
|
||||
assertTrue(config.isEnableHttpPostArgs());
|
||||
}
|
||||
|
||||
@@ -60,10 +57,7 @@ public class HgConfigDtoToHgConfigMapperTest {
|
||||
configDto.setDisabled(true);
|
||||
configDto.setEncoding("ABC");
|
||||
configDto.setHgBinary("/etc/hg");
|
||||
configDto.setPythonBinary("/py");
|
||||
configDto.setPythonPath("/etc/");
|
||||
configDto.setShowRevisionInId(true);
|
||||
configDto.setUseOptimizedBytecode(true);
|
||||
configDto.setEnableHttpPostArgs(true);
|
||||
|
||||
return configDto;
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.github.sdorra.shiro.ShiroRule;
|
||||
import com.github.sdorra.shiro.SubjectAware;
|
||||
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||
import org.jboss.resteasy.mock.MockHttpResponse;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.web.RestDispatcher;
|
||||
|
||||
import javax.inject.Provider;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SubjectAware(
|
||||
configuration = "classpath:sonia/scm/configuration/shiro.ini",
|
||||
password = "secret"
|
||||
)
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HgConfigInstallationsResourceTest {
|
||||
|
||||
@Rule
|
||||
public ShiroRule shiro = new ShiroRule();
|
||||
|
||||
private RestDispatcher dispatcher = new RestDispatcher();
|
||||
|
||||
private final URI baseUri = URI.create("/");
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private ScmPathInfoStore scmPathInfoStore;
|
||||
|
||||
@InjectMocks
|
||||
private HgConfigInstallationsToDtoMapper mapper;
|
||||
|
||||
@Mock
|
||||
private Provider<HgConfigInstallationsResource> resourceProvider;
|
||||
|
||||
|
||||
@Before
|
||||
public void prepareEnvironment() {
|
||||
HgConfigInstallationsResource resource = new HgConfigInstallationsResource(mapper);
|
||||
|
||||
when(resourceProvider.get()).thenReturn(resource);
|
||||
dispatcher.addSingletonResource(
|
||||
new HgConfigResource(null, null, null, null,
|
||||
null, resourceProvider));
|
||||
|
||||
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "readOnly")
|
||||
public void shouldGetHgInstallations() throws Exception {
|
||||
MockHttpResponse response = get("hg");
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
|
||||
String contentAsString = response.getContentAsString();
|
||||
assertThat(contentAsString).contains("{\"paths\":[");
|
||||
assertThat(contentAsString).contains("hg");
|
||||
assertThat(contentAsString).doesNotContain("python");
|
||||
|
||||
assertThat(contentAsString).contains("\"self\":{\"href\":\"/v2/config/hg/installations/hg");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "writeOnly")
|
||||
public void shouldNotGetHgInstallationsWhenNotAuthorized() throws Exception {
|
||||
MockHttpResponse response = get("hg");
|
||||
|
||||
assertEquals("Subject does not have permission [configuration:read:hg]", response.getContentAsString());
|
||||
assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "readOnly")
|
||||
public void shouldGetPythonInstallations() throws Exception {
|
||||
MockHttpResponse response = get("python");
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
|
||||
String contentAsString = response.getContentAsString();
|
||||
assertThat(contentAsString).contains("{\"paths\":[");
|
||||
assertThat(contentAsString).contains("python");
|
||||
|
||||
assertThat(contentAsString).contains("\"self\":{\"href\":\"/v2/config/hg/installations/python");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "writeOnly")
|
||||
public void shouldNotGetPythonInstallationsWhenNotAuthorized() throws Exception {
|
||||
MockHttpResponse response = get("python");
|
||||
|
||||
assertEquals("Subject does not have permission [configuration:read:hg]", response.getContentAsString());
|
||||
assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus());
|
||||
}
|
||||
|
||||
private MockHttpResponse get(String path) throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + HgConfigResource.HG_CONFIG_PATH_V2 + "/installations/" + path);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
dispatcher.invoke(request, response);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.github.sdorra.shiro.ShiroRule;
|
||||
import com.github.sdorra.shiro.SubjectAware;
|
||||
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||
import org.jboss.resteasy.mock.MockHttpResponse;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.installer.HgPackage;
|
||||
import sonia.scm.installer.HgPackageReader;
|
||||
import sonia.scm.net.ahc.AdvancedHttpClient;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
import sonia.scm.web.RestDispatcher;
|
||||
|
||||
import javax.inject.Provider;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static sonia.scm.api.v2.resources.HgConfigTests.createPackage;
|
||||
|
||||
@SubjectAware(
|
||||
configuration = "classpath:sonia/scm/configuration/shiro.ini",
|
||||
password = "secret"
|
||||
)
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HgConfigPackageResourceTest {
|
||||
|
||||
public static final String URI = "/" + HgConfigResource.HG_CONFIG_PATH_V2 + "/packages";
|
||||
@Rule
|
||||
public ShiroRule shiro = new ShiroRule();
|
||||
|
||||
private RestDispatcher dispatcher = new RestDispatcher();
|
||||
|
||||
private final URI baseUri = java.net.URI.create("/");
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private ScmPathInfoStore scmPathInfoStore;
|
||||
|
||||
@InjectMocks
|
||||
private HgConfigPackagesToDtoMapperImpl mapper;
|
||||
|
||||
@Mock
|
||||
private HgRepositoryHandler repositoryHandler;
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private HgPackageReader hgPackageReader;
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private AdvancedHttpClient advancedHttpClient;
|
||||
|
||||
@Mock
|
||||
private Provider<HgConfigPackageResource> hgConfigPackageResourceProvider;
|
||||
|
||||
@Mock
|
||||
private HgPackage hgPackage;
|
||||
|
||||
@Before
|
||||
public void prepareEnvironment() {
|
||||
setupResources();
|
||||
|
||||
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||
|
||||
when(hgPackageReader.getPackages().getPackages()).thenReturn(createPackages());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "readOnly")
|
||||
public void shouldGetPackages() throws Exception {
|
||||
MockHttpResponse response = get();
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
|
||||
String responseString = response.getContentAsString();
|
||||
ObjectNode responseJson = new ObjectMapper().readValue(responseString, ObjectNode.class);
|
||||
|
||||
JsonNode packages = responseJson.get("packages");
|
||||
assertThat(packages).isNotNull();
|
||||
assertThat(packages).hasSize(2);
|
||||
|
||||
JsonNode package1 = packages.get(0);
|
||||
assertThat(package1.get("_links")).isNull();
|
||||
|
||||
JsonNode hgConfigTemplate = package1.get("hgConfigTemplate");
|
||||
assertThat(hgConfigTemplate).isNotNull();
|
||||
assertThat(hgConfigTemplate.get("_links")).isNull();
|
||||
|
||||
assertThat(responseString).contains("\"_links\":{\"self\":{\"href\":\"/v2/config/hg/packages");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "writeOnly")
|
||||
public void shouldNotGetPackagesWhenNotAuthorized() throws Exception {
|
||||
MockHttpResponse response = get();
|
||||
|
||||
assertEquals("Subject does not have permission [configuration:read:hg]", response.getContentAsString());
|
||||
assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "writeOnly")
|
||||
public void shouldInstallPackage() throws Exception {
|
||||
String packgeId = "ourPackage";
|
||||
String url = "http://url";
|
||||
|
||||
setupPackageInstallation(packgeId, url);
|
||||
when(advancedHttpClient.get(url).request().contentAsStream())
|
||||
.thenReturn(new ByteArrayInputStream("mockedFile".getBytes()));
|
||||
|
||||
MockHttpResponse response = put(packgeId);
|
||||
assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "writeOnly")
|
||||
public void shouldHandleFailingInstallation() throws Exception {
|
||||
String packgeId = "ourPackage";
|
||||
String url = "http://url";
|
||||
|
||||
setupPackageInstallation(packgeId, url);
|
||||
when(advancedHttpClient.get(url).request().contentAsStream())
|
||||
.thenThrow(new IOException("mocked Exception"));
|
||||
|
||||
MockHttpResponse response = put(packgeId);
|
||||
assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "writeOnly")
|
||||
public void shouldHandlePackagesThatAreNotFound() throws Exception {
|
||||
String packageId = "this-package-does-not-ex";
|
||||
when(hgPackageReader.getPackage(packageId)).thenReturn(null);
|
||||
MockHttpResponse response = put(packageId);
|
||||
assertEquals(HttpServletResponse.SC_NOT_FOUND, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "readOnly")
|
||||
public void shouldNotInstallPackageWhenNotAuthorized() throws Exception {
|
||||
MockHttpResponse response = put("don-t-care");
|
||||
|
||||
assertEquals("Subject does not have permission [configuration:write:hg]", response.getContentAsString());
|
||||
assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus());
|
||||
}
|
||||
|
||||
private List<HgPackage> createPackages() {
|
||||
return Arrays.asList(createPackage(), new HgPackage());
|
||||
}
|
||||
|
||||
private MockHttpResponse get() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get(URI);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
dispatcher.invoke(request, response);
|
||||
return response;
|
||||
}
|
||||
|
||||
private MockHttpResponse put(String pckgId) throws URISyntaxException {
|
||||
String packgeIdParam = "";
|
||||
if (pckgId != null) {
|
||||
packgeIdParam = "/" + pckgId;
|
||||
}
|
||||
MockHttpRequest request = MockHttpRequest.put(URI + packgeIdParam);
|
||||
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
dispatcher.invoke(request, response);
|
||||
return response;
|
||||
}
|
||||
|
||||
private void setupResources() {
|
||||
HgConfigPackageResource hgConfigPackageResource =
|
||||
new HgConfigPackageResource(hgPackageReader, advancedHttpClient, repositoryHandler, mapper);
|
||||
|
||||
when(hgConfigPackageResourceProvider.get()).thenReturn(hgConfigPackageResource);
|
||||
dispatcher.addSingletonResource(
|
||||
new HgConfigResource(null, null, null,
|
||||
hgConfigPackageResourceProvider, null, null));
|
||||
}
|
||||
|
||||
private void setupPackageInstallation(String packgeId, String url) throws IOException {
|
||||
when(hgPackage.getId()).thenReturn(packgeId);
|
||||
when(hgPackageReader.getPackage(packgeId)).thenReturn(hgPackage);
|
||||
when(repositoryHandler.getConfig()).thenReturn(new HgConfig());
|
||||
when(hgPackage.getHgConfigTemplate()).thenReturn(new HgConfig());
|
||||
when(hgPackage.getUrl()).thenReturn(url);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.installer.HgPackage;
|
||||
import sonia.scm.installer.HgPackages;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static sonia.scm.api.v2.resources.HgConfigTests.assertEqualsPackage;
|
||||
import static sonia.scm.api.v2.resources.HgConfigTests.createPackage;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HgConfigPackagesToDtoMapperTest {
|
||||
|
||||
private URI baseUri = URI.create("http://example.com/base/");
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private ScmPathInfoStore scmPathInfoStore;
|
||||
|
||||
@InjectMocks
|
||||
private HgConfigPackagesToDtoMapperImpl mapper;
|
||||
|
||||
private URI expectedBaseUri;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||
expectedBaseUri = baseUri.resolve(HgConfigResource.HG_CONFIG_PATH_V2 + "/packages");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapFields() {
|
||||
HgPackages hgPackages = new HgPackages();
|
||||
hgPackages.setPackages(createPackages());
|
||||
|
||||
HgConfigPackagesDto dto = mapper.map(hgPackages);
|
||||
|
||||
assertThat(dto.getPackages()).hasSize(2);
|
||||
|
||||
HgConfigPackagesDto.HgConfigPackageDto hgPackageDto1 = dto.getPackages().get(0);
|
||||
assertEqualsPackage(hgPackageDto1);
|
||||
|
||||
HgConfigPackagesDto.HgConfigPackageDto hgPackageDto2 = dto.getPackages().get(1);
|
||||
// Just verify a random field
|
||||
assertThat(hgPackageDto2.getId()).isNull();
|
||||
|
||||
assertEquals(expectedBaseUri.toString(), dto.getLinks().getLinkBy("self").get().getHref());
|
||||
}
|
||||
|
||||
|
||||
private List<HgPackage> createPackages() {
|
||||
return Arrays.asList(createPackage(), new HgPackage());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -81,22 +81,15 @@ public class HgConfigResourceTest {
|
||||
@Mock
|
||||
private HgRepositoryHandler repositoryHandler;
|
||||
|
||||
@Mock
|
||||
private Provider<HgConfigPackageResource> packagesResource;
|
||||
|
||||
@Mock
|
||||
private Provider<HgConfigAutoConfigurationResource> autoconfigResource;
|
||||
|
||||
@Mock
|
||||
private Provider<HgConfigInstallationsResource> installationsResource;
|
||||
|
||||
@Before
|
||||
public void prepareEnvironment() {
|
||||
HgConfig gitConfig = createConfiguration();
|
||||
when(repositoryHandler.getConfig()).thenReturn(gitConfig);
|
||||
HgConfigResource gitConfigResource =
|
||||
new HgConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler, packagesResource,
|
||||
autoconfigResource, installationsResource);
|
||||
new HgConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler, autoconfigResource);
|
||||
dispatcher.addSingletonResource(gitConfigResource);
|
||||
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||
}
|
||||
|
||||
@@ -21,10 +21,9 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import sonia.scm.installer.HgPackage;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -41,10 +40,7 @@ class HgConfigTests {
|
||||
|
||||
config.setEncoding("ABC");
|
||||
config.setHgBinary("/etc/hg");
|
||||
config.setPythonBinary("/py");
|
||||
config.setPythonPath("/etc/");
|
||||
config.setShowRevisionInId(true);
|
||||
config.setUseOptimizedBytecode(true);
|
||||
|
||||
return config;
|
||||
}
|
||||
@@ -54,36 +50,7 @@ class HgConfigTests {
|
||||
|
||||
assertEquals("ABC", dto.getEncoding());
|
||||
assertEquals("/etc/hg", dto.getHgBinary());
|
||||
assertEquals("/py", dto.getPythonBinary());
|
||||
assertEquals("/etc/", dto.getPythonPath());
|
||||
assertTrue(dto.isShowRevisionInId());
|
||||
assertTrue(dto.isUseOptimizedBytecode());
|
||||
}
|
||||
|
||||
static HgPackage createPackage() {
|
||||
HgPackage hgPackage= new HgPackage();
|
||||
hgPackage.setArch("arch");
|
||||
hgPackage.setId("1");
|
||||
hgPackage.setHgVersion("2");
|
||||
hgPackage.setPlatform("someOs");
|
||||
hgPackage.setPythonVersion("3");
|
||||
hgPackage.setSize(4);
|
||||
hgPackage.setUrl("https://package");
|
||||
hgPackage.setHgConfigTemplate(createConfiguration());
|
||||
return hgPackage;
|
||||
}
|
||||
|
||||
static void assertEqualsPackage(HgConfigPackagesDto.HgConfigPackageDto dto) {
|
||||
assertEquals("arch", dto.getArch());
|
||||
assertEquals("1", dto.getId());
|
||||
assertEquals("2", dto.getHgVersion());
|
||||
assertEquals("someOs", dto.getPlatform());
|
||||
assertEquals("3", dto.getPythonVersion());
|
||||
assertEquals(4, dto.getSize());
|
||||
assertEquals("https://package", dto.getUrl());
|
||||
|
||||
assertEqualsConfiguration(dto.getHgConfigTemplate());
|
||||
assertTrue(dto.getHgConfigTemplate().getLinks().isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,27 +22,21 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository;
|
||||
package sonia.scm.autoconfig;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@XmlRootElement(name = "version")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class HgVersion {
|
||||
class AutoConfigModuleTest {
|
||||
|
||||
public static final String UNKNOWN = "x.y.z (unknown)";
|
||||
@Test
|
||||
void shouldBindAutoConfigurator() {
|
||||
Injector injector = Guice.createInjector(new AutoConfigModule());
|
||||
AutoConfigurator configurator = injector.getInstance(AutoConfigurator.class);
|
||||
assertThat(configurator).isNotNull();
|
||||
}
|
||||
|
||||
private String mercurial;
|
||||
private String python;
|
||||
}
|
||||
@@ -21,55 +21,48 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
package sonia.scm.autoconfig;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.Platform;
|
||||
import sonia.scm.repository.HgVerifier;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HgConfigInstallationsToDtoMapperTest {
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AutoConfiguratorProviderTest {
|
||||
|
||||
@Mock
|
||||
private HgVerifier verifier;
|
||||
|
||||
private URI baseUri = URI.create("http://example.com/base/");
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private ScmPathInfoStore scmPathInfoStore;
|
||||
@Mock
|
||||
private Platform platform;
|
||||
|
||||
@InjectMocks
|
||||
private HgConfigInstallationsToDtoMapper mapper;
|
||||
private AutoConfiguratorProvider provider;
|
||||
|
||||
private URI expectedBaseUri;
|
||||
@Test
|
||||
void shouldReturnPosixAutoConfiguration() {
|
||||
when(platform.isPosix()).thenReturn(true);
|
||||
|
||||
private String expectedPath = "path";
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||
expectedBaseUri = baseUri.resolve(HgConfigResource.HG_CONFIG_PATH_V2 + "/installations/" + expectedPath);
|
||||
assertThat(provider.get()).isInstanceOf(PosixAutoConfigurator.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapFields() {
|
||||
List<String> installations = Arrays.asList("/hg", "/bin/hg");
|
||||
void shouldReturnWindowsAutoConfiguration() {
|
||||
when(platform.isWindows()).thenReturn(true);
|
||||
|
||||
HgConfigInstallationsDto dto = mapper.map(installations, expectedPath);
|
||||
assertThat(provider.get()).isInstanceOf(WindowsAutoConfigurator.class);
|
||||
}
|
||||
|
||||
assertThat(dto.getPaths()).isEqualTo(installations);
|
||||
|
||||
assertEquals(expectedBaseUri.toString(), dto.getLinks().getLinkBy("self").get().getHref());
|
||||
@Test
|
||||
void shouldReturnNoOpAutoConfiguration() {
|
||||
assertThat(provider.get()).isInstanceOf(NoOpAutoConfigurator.class);
|
||||
}
|
||||
}
|
||||
@@ -28,139 +28,101 @@ import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.HgVerifier;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PosixAutoConfiguratorTest {
|
||||
|
||||
@Test
|
||||
void shouldConfigureWithShebangPath(@TempDir Path directory) throws IOException {
|
||||
Path hg = directory.resolve("hg");
|
||||
Path python = directory.resolve("python");
|
||||
@Mock
|
||||
private HgVerifier verifier;
|
||||
|
||||
Files.write(hg, ("#!" + python.toAbsolutePath().toString()).getBytes(StandardCharsets.UTF_8));
|
||||
Files.createFile(python);
|
||||
@Test
|
||||
void shouldConfigureMercurial(@TempDir Path directory) {
|
||||
Path hg = directory.resolve("hg");
|
||||
when(verifier.isValid(hg)).thenReturn(true);
|
||||
|
||||
PosixAutoConfigurator configurator = create(directory);
|
||||
HgConfig config = configurator.configure();
|
||||
|
||||
HgConfig config = new HgConfig();
|
||||
configurator.configure(config);
|
||||
|
||||
assertThat(config.getHgBinary()).isEqualTo(hg.toString());
|
||||
assertThat(config.getPythonBinary()).isEqualTo(python.toString());
|
||||
}
|
||||
|
||||
private PosixAutoConfigurator create(@TempDir Path directory) {
|
||||
return new PosixAutoConfigurator(createEnv(directory), Collections.emptyList());
|
||||
private PosixAutoConfigurator create(Path directory) {
|
||||
return new PosixAutoConfigurator(verifier, createEnv(directory), Collections.emptyList());
|
||||
}
|
||||
|
||||
private Map<String, String> createEnv(Path... paths) {
|
||||
return ImmutableMap.of("PATH", Joiner.on(File.pathSeparator).join(paths));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConfigureWithShebangEnv(@TempDir Path directory) throws IOException {
|
||||
Path hg = directory.resolve("hg");
|
||||
Path python = directory.resolve("python3.8");
|
||||
|
||||
Files.write(hg, "#!/usr/bin/env python3.8".getBytes(StandardCharsets.UTF_8));
|
||||
Files.createFile(python);
|
||||
|
||||
PosixAutoConfigurator configurator = create(directory);
|
||||
HgConfig config = configurator.configure();
|
||||
|
||||
assertThat(config.getHgBinary()).isEqualTo(hg.toString());
|
||||
assertThat(config.getPythonBinary()).isEqualTo(python.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConfigureWithoutShebang(@TempDir Path directory) throws IOException {
|
||||
Path hg = directory.resolve("hg");
|
||||
Path python = directory.resolve("python");
|
||||
|
||||
Files.createFile(hg);
|
||||
Files.createFile(python);
|
||||
|
||||
PosixAutoConfigurator configurator = create(directory);
|
||||
HgConfig config = configurator.configure();
|
||||
|
||||
assertThat(config.getHgBinary()).isEqualTo(hg.toString());
|
||||
assertThat(config.getPythonBinary()).isEqualTo(python.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConfigureWithoutShebangButWithPython3(@TempDir Path directory) throws IOException {
|
||||
Path hg = directory.resolve("hg");
|
||||
Path python = directory.resolve("python3");
|
||||
|
||||
Files.createFile(hg);
|
||||
Files.createFile(python);
|
||||
|
||||
PosixAutoConfigurator configurator = create(directory);
|
||||
HgConfig config = configurator.configure();
|
||||
|
||||
assertThat(config.getHgBinary()).isEqualTo(hg.toString());
|
||||
assertThat(config.getPythonBinary()).isEqualTo(python.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConfigureFindPythonInAdditionalPath(@TempDir Path directory) throws IOException {
|
||||
void shouldFindMercurialInAdditionalPath(@TempDir Path directory) {
|
||||
Path def = directory.resolve("default");
|
||||
Files.createDirectory(def);
|
||||
Path additional = directory.resolve("additional");
|
||||
Files.createDirectory(additional);
|
||||
|
||||
Path hg = def.resolve("hg");
|
||||
Path python = additional.resolve("python");
|
||||
|
||||
Files.createFile(hg);
|
||||
Files.createFile(python);
|
||||
when(verifier.isValid(hg)).thenReturn(true);
|
||||
|
||||
PosixAutoConfigurator configurator = new PosixAutoConfigurator(
|
||||
createEnv(def), ImmutableList.of(additional.toAbsolutePath().toString())
|
||||
verifier, createEnv(def), ImmutableList.of(additional.toAbsolutePath().toString())
|
||||
);
|
||||
|
||||
HgConfig config = configurator.configure();
|
||||
HgConfig config = new HgConfig();
|
||||
configurator.configure(config);
|
||||
|
||||
assertThat(config.getHgBinary()).isEqualTo(hg.toString());
|
||||
assertThat(config.getPythonBinary()).isEqualTo(python.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindModulePathFromDebuginstallOutput(@TempDir Path directory) throws IOException {
|
||||
Path hg = directory.resolve("hg");
|
||||
Files.createFile(hg);
|
||||
hg.toFile().setExecutable(true);
|
||||
void shouldSkipInvalidMercurialInstallations(@TempDir Path directory) {
|
||||
Path one = directory.resolve("one");
|
||||
Path two = directory.resolve("two");
|
||||
Path three = directory.resolve("three");
|
||||
Path hg = three.resolve("hg");
|
||||
|
||||
Path modules = directory.resolve("modules");
|
||||
Files.createDirectories(modules);
|
||||
|
||||
Path mercurialModule = modules.resolve("mercurial");
|
||||
Files.createDirectories(mercurialModule);
|
||||
|
||||
PosixAutoConfigurator configurator = create(directory);
|
||||
configurator.setExecutor((Path binary, String... args) -> {
|
||||
String content = String.join("\n",
|
||||
"checking Python executable (/python3.8)",
|
||||
"checking Python lib (/python3.8)...",
|
||||
"checking installed modules (" + mercurialModule.toString() + ")...",
|
||||
"checking templates (/mercurial/templates)...",
|
||||
"checking default template (/mercurial/templates/map-cmdline.default))"
|
||||
);
|
||||
return new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
|
||||
when(verifier.isValid(any(Path.class))).then(ic -> {
|
||||
Path path = ic.getArgument(0, Path.class);
|
||||
return path.equals(hg);
|
||||
});
|
||||
HgConfig config = configurator.configure();
|
||||
|
||||
PosixAutoConfigurator configurator = new PosixAutoConfigurator(
|
||||
verifier, createEnv(one), ImmutableList.of(
|
||||
two.toAbsolutePath().toString(),
|
||||
three.toAbsolutePath().toString()
|
||||
)
|
||||
);
|
||||
|
||||
HgConfig config = new HgConfig();
|
||||
configurator.configure(config);
|
||||
|
||||
assertThat(config.getHgBinary()).isEqualTo(hg.toString());
|
||||
assertThat(config.getPythonPath()).isEqualTo(modules.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotConfigureMercurial(@TempDir Path directory) {
|
||||
PosixAutoConfigurator configurator = create(directory);
|
||||
|
||||
HgConfig config = new HgConfig();
|
||||
configurator.configure(config);
|
||||
|
||||
assertThat(config.getHgBinary()).isNull();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.autoconfig;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.HgVerifier;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static sonia.scm.autoconfig.WindowsAutoConfigurator.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class WindowsAutoConfiguratorTest {
|
||||
|
||||
@Mock
|
||||
private HgVerifier verifier;
|
||||
|
||||
@Mock
|
||||
private WindowsRegistry registry;
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { BINARY_HG_EXE, BINARY_HG_BAT })
|
||||
void shouldConfigureHgFromPath(String binary, @TempDir Path directory) {
|
||||
Path hgPath = directory.resolve(binary);
|
||||
mockIsValid(hgPath);
|
||||
|
||||
String hg = configure(directory);
|
||||
assertThat(hg).isEqualTo(hgPath.toString());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { BINARY_HG_EXE, BINARY_HG_BAT })
|
||||
void shouldConfigureHgFromSecondPath(String binary, @TempDir Path directory) {
|
||||
Path one = directory.resolve("one");
|
||||
Path two = directory.resolve("two");
|
||||
Path hgPath = two.resolve(binary);
|
||||
mockIsValid(hgPath);
|
||||
|
||||
String hg = configure(one, two);
|
||||
assertThat(hg).isEqualTo(hgPath.toString());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { REGISTRY_KEY_TORTOISE_HG, REGISTRY_KEY_MERCURIAL })
|
||||
void shouldConfigureHgFromHgInstallation(String registryKey, @TempDir Path directory) {
|
||||
Path one = directory.resolve("one");
|
||||
Path two = directory.resolve("two");
|
||||
Path hgPath = two.resolve(BINARY_HG_EXE);
|
||||
|
||||
mockIsValid(hgPath);
|
||||
mockRegistryKey(registryKey, two.toString());
|
||||
|
||||
String hg = configure(one);
|
||||
assertThat(hg).isEqualTo(hgPath.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotConfigureMercurial(@TempDir Path directory) {
|
||||
String hg = configure(directory);
|
||||
assertThat(hg).isNull();
|
||||
}
|
||||
|
||||
private void mockRegistryKey(String key, String value) {
|
||||
when(registry.get(anyString())).then(ic -> {
|
||||
String k = ic.getArgument(0, String.class);
|
||||
if (key.equals(k)) {
|
||||
return Optional.of(value);
|
||||
}
|
||||
return Optional.empty();
|
||||
});
|
||||
}
|
||||
|
||||
private void mockIsValid(Path path) {
|
||||
when(verifier.isValid(any(Path.class))).then(ic -> {
|
||||
Path p = ic.getArgument(0, Path.class);
|
||||
return path.equals(p);
|
||||
});
|
||||
}
|
||||
|
||||
private String path(Path... paths) {
|
||||
return Arrays.stream(paths)
|
||||
.map(Path::toString)
|
||||
.collect(
|
||||
Collectors.joining(File.pathSeparator)
|
||||
);
|
||||
}
|
||||
|
||||
private String configure(Path... paths) {
|
||||
return configure(path(paths));
|
||||
}
|
||||
|
||||
private String configure(String path) {
|
||||
HgConfig config = new HgConfig();
|
||||
configurator(path).configure(config);
|
||||
return config.getHgBinary();
|
||||
}
|
||||
|
||||
private WindowsAutoConfigurator configurator(String path) {
|
||||
Map<String, String> env = new HashMap<>();
|
||||
env.put(ENV_PATH, path);
|
||||
return new WindowsAutoConfigurator(verifier, registry, env);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,7 +53,6 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static sonia.scm.repository.DefaultHgEnvironmentBuilder.*;
|
||||
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DefaultHgEnvironmentBuilderTest {
|
||||
|
||||
@@ -83,18 +82,18 @@ class DefaultHgEnvironmentBuilderTest {
|
||||
|
||||
@Test
|
||||
void shouldReturnReadEnvironment() {
|
||||
Repository heartOfGold = prepareForRead("/usr/lib/python", "42");
|
||||
Repository heartOfGold = prepareForRead("42");
|
||||
|
||||
Map<String, String> env = builder.read(heartOfGold);
|
||||
assertReadEnv(env, "/usr/lib/python", "42");
|
||||
assertReadEnv(env, "42");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnWriteEnvironment() throws IOException {
|
||||
Repository heartOfGold = prepareForWrite("/opt/python", "21");
|
||||
Repository heartOfGold = prepareForWrite("21");
|
||||
|
||||
Map<String, String> env = builder.write(heartOfGold);
|
||||
assertReadEnv(env, "/opt/python", "21");
|
||||
assertReadEnv(env, "21");
|
||||
|
||||
String bearer = CipherUtil.getInstance().decode(env.get(ENV_BEARER_TOKEN));
|
||||
assertThat(bearer).isEqualTo("secretAC");
|
||||
@@ -106,7 +105,7 @@ class DefaultHgEnvironmentBuilderTest {
|
||||
@Test
|
||||
void shouldSetTransactionId() throws IOException {
|
||||
TransactionId.set("ti42");
|
||||
Repository heartOfGold = prepareForWrite("/opt/python", "21");
|
||||
Repository heartOfGold = prepareForWrite("21");
|
||||
Map<String, String> env = builder.write(heartOfGold);
|
||||
assertThat(env).containsEntry(ENV_TRANSACTION_ID, "ti42");
|
||||
}
|
||||
@@ -114,12 +113,12 @@ class DefaultHgEnvironmentBuilderTest {
|
||||
@Test
|
||||
void shouldThrowIllegalStateIfServerCouldNotBeStarted() throws IOException {
|
||||
when(server.start()).thenThrow(new IOException("failed to start"));
|
||||
Repository repository = prepareForRead("/usr", "42");
|
||||
Repository repository = prepareForRead("42");
|
||||
assertThrows(IllegalStateException.class, () -> builder.write(repository));
|
||||
}
|
||||
|
||||
private Repository prepareForWrite(String pythonPath, String id) throws IOException {
|
||||
Repository heartOfGold = prepareForRead(pythonPath, id);
|
||||
private Repository prepareForWrite(String id) throws IOException {
|
||||
Repository heartOfGold = prepareForRead(id);
|
||||
applyAccessToken("secretAC");
|
||||
when(server.start()).thenReturn(2042);
|
||||
when(hookEnvironment.getChallenge()).thenReturn("challenge");
|
||||
@@ -133,21 +132,19 @@ class DefaultHgEnvironmentBuilderTest {
|
||||
}
|
||||
|
||||
|
||||
private void assertReadEnv(Map<String, String> env, String pythonPath, String repositoryId) {
|
||||
private void assertReadEnv(Map<String, String> env, String repositoryId) {
|
||||
assertThat(env)
|
||||
.containsEntry(ENV_REPOSITORY_ID, repositoryId)
|
||||
.containsEntry(ENV_REPOSITORY_NAME, "hitchhiker/HeartOfGold")
|
||||
.containsEntry(ENV_HTTP_POST_ARGS, "false")
|
||||
.containsEntry(ENV_REPOSITORY_PATH, directory.resolve("repo").toAbsolutePath().toString())
|
||||
.containsEntry(ENV_PYTHON_PATH, pythonPath + File.pathSeparator + directory.resolve("home/lib/python"));
|
||||
.containsEntry(ENV_REPOSITORY_PATH, directory.resolve("repo").toAbsolutePath().toString());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private Repository prepareForRead(String pythonPath, String id) {
|
||||
private Repository prepareForRead(String id) {
|
||||
when(repositoryHandler.getDirectory(id)).thenReturn(directory.resolve("repo").toFile());
|
||||
|
||||
HgConfig config = new HgConfig();
|
||||
config.setPythonPath(pythonPath);
|
||||
when(repositoryHandler.getConfig()).thenReturn(config);
|
||||
|
||||
Repository heartOfGold = RepositoryTestData.createHeartOfGold();
|
||||
|
||||
@@ -32,10 +32,12 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.autoconfig.AutoConfiguratorProvider;
|
||||
import sonia.scm.plugin.PluginLoader;
|
||||
import sonia.scm.repository.spi.HgVersionCommand;
|
||||
import sonia.scm.store.ConfigurationStoreFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.File;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -71,7 +73,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
|
||||
@Override
|
||||
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) {
|
||||
HgRepositoryHandler handler = new HgRepositoryHandler(factory, locationResolver, null, null);
|
||||
HgRepositoryHandler handler = createHandler(factory, locationResolver);
|
||||
|
||||
handler.init(contextProvider);
|
||||
HgTestUtil.checkForSkip(handler);
|
||||
@@ -81,11 +83,10 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
|
||||
@Test
|
||||
public void getDirectory() {
|
||||
HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, locationResolver, null, null);
|
||||
HgRepositoryHandler repositoryHandler = createHandler(factory, locationResolver);
|
||||
|
||||
HgConfig hgConfig = new HgConfig();
|
||||
hgConfig.setHgBinary("hg");
|
||||
hgConfig.setPythonBinary("python");
|
||||
repositoryHandler.setConfig(hgConfig);
|
||||
|
||||
initRepository();
|
||||
@@ -93,16 +94,22 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
assertEquals(repoPath.toString() + File.separator + RepositoryDirectoryHandler.REPOSITORIES_NATIVE_DIRECTORY, path.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private HgRepositoryHandler createHandler(ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver) {
|
||||
AutoConfiguratorProvider provider = new AutoConfiguratorProvider(new HgVerifier());
|
||||
return new HgRepositoryHandler(factory, locationResolver, null, null, provider.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnVersionInformation() {
|
||||
PluginLoader pluginLoader = mock(PluginLoader.class);
|
||||
when(pluginLoader.getUberClassLoader()).thenReturn(HgRepositoryHandler.class.getClassLoader());
|
||||
|
||||
HgVersionCommand versionCommand = mock(HgVersionCommand.class);
|
||||
when(versionCommand.get()).thenReturn(new HgVersion("5.2.0", "3.7.2"));
|
||||
when(versionCommand.get()).thenReturn("python/3.7.2 mercurial/5.2.0");
|
||||
|
||||
HgRepositoryHandler handler = new HgRepositoryHandler(
|
||||
factory, locationResolver, pluginLoader, null
|
||||
factory, locationResolver, pluginLoader, null, new AutoConfiguratorProvider(new HgVerifier()).get()
|
||||
);
|
||||
|
||||
String versionInformation = handler.getVersionInformation(versionCommand);
|
||||
|
||||
@@ -29,6 +29,7 @@ package sonia.scm.repository;
|
||||
import org.junit.Assume;
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.TempDirRepositoryLocationResolver;
|
||||
import sonia.scm.autoconfig.AutoConfiguratorProvider;
|
||||
import sonia.scm.repository.hooks.HookEnvironment;
|
||||
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
||||
|
||||
@@ -87,7 +88,8 @@ public final class HgTestUtil
|
||||
new InMemoryConfigurationStoreFactory(),
|
||||
repositoryLocationResolver,
|
||||
null,
|
||||
null
|
||||
null,
|
||||
new AutoConfiguratorProvider(new HgVerifier()).get()
|
||||
);
|
||||
handler.init(context);
|
||||
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import org.assertj.core.util.Strings;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import sonia.scm.util.SystemUtil;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class HgVerifierTest {
|
||||
|
||||
@Nested
|
||||
class WithDefaultVersionResolver {
|
||||
|
||||
private final HgVerifier verifier = new HgVerifier();
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseIfFileDoesNotExists(@TempDir Path directory) {
|
||||
Path hg = directory.resolve("hg");
|
||||
|
||||
boolean isValid = verify(hg);
|
||||
|
||||
assertThat(isValid).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseIfFileIsADirectory(@TempDir Path directory) throws IOException {
|
||||
Path hg = directory.resolve("hg");
|
||||
Files.createDirectories(hg);
|
||||
|
||||
boolean isValid = verify(hg);
|
||||
|
||||
assertThat(isValid).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseIfFileIsNotExecutable(@TempDir Path directory) throws IOException {
|
||||
Path hg = directory.resolve("hg");
|
||||
Files.createFile(hg);
|
||||
|
||||
boolean isValid = verify(hg);
|
||||
|
||||
assertThat(isValid).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrueForIfMercurialIsAvailable() {
|
||||
Path hg = findHg();
|
||||
|
||||
// skip test if we could not find mercurial
|
||||
Assumptions.assumeTrue(hg != null, "skip test, because we could not find mercurial on path");
|
||||
|
||||
boolean isValid = verify(hg);
|
||||
|
||||
assertThat(isValid).isTrue();
|
||||
}
|
||||
|
||||
private Path findHg() {
|
||||
String pathEnv = System.getenv("PATH");
|
||||
if (Strings.isNullOrEmpty(pathEnv)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (String path : Splitter.on(File.pathSeparator).splitToList(pathEnv)) {
|
||||
Path hg = Paths.get(path, SystemUtil.isWindows() ? "hg.exe" : "hg");
|
||||
if (Files.exists(hg)) {
|
||||
return hg;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean verify(Path hg) {
|
||||
HgConfig config = new HgConfig();
|
||||
config.setHgBinary(hg.toString());
|
||||
return verifier.isValid(config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "3-2-1", "x.y.z", "3.2.0" })
|
||||
void shouldReturnFalseForInvalidVersions(String version, @TempDir Path directory) throws IOException {
|
||||
HgVerifier verifier = new HgVerifier(hg -> version);
|
||||
|
||||
Path hg = createHg(directory);
|
||||
|
||||
boolean isValid = verifier.isValid(hg);
|
||||
|
||||
assertThat(isValid).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseOnIOException(@TempDir Path directory) throws IOException {
|
||||
HgVerifier verifier = new HgVerifier(hg -> {
|
||||
throw new IOException("failed");
|
||||
});
|
||||
|
||||
Path hg = createHg(directory);
|
||||
|
||||
boolean isValid = verifier.isValid(hg);
|
||||
|
||||
assertThat(isValid).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrue(@TempDir Path directory) throws IOException {
|
||||
HgVerifier verifier = new HgVerifier(hg -> "4.2.0");
|
||||
|
||||
Path hg = createHg(directory);
|
||||
|
||||
boolean isValid = verifier.isValid(hg);
|
||||
|
||||
assertThat(isValid).isTrue();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private Path createHg(Path directory) throws IOException {
|
||||
Path hg = directory.resolve("hg");
|
||||
Files.createFile(hg);
|
||||
|
||||
// skip test if we could not set executable flag
|
||||
Assumptions.assumeTrue(hg.toFile().setExecutable(true));
|
||||
return hg;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class HgWindowsPackageFixTest
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void testHgBatModify() throws IOException
|
||||
{
|
||||
testModify(createHgBat("01"));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void testHgBatModifyWithComment() throws IOException
|
||||
{
|
||||
testModify(createHgBat("02"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void testHgBatWithoutModify() throws IOException
|
||||
{
|
||||
long length = testModify(createHgBat("03")).length();
|
||||
|
||||
assertEquals(0, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param number
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private File createHgBat(String number) throws IOException
|
||||
{
|
||||
URL url = Resources.getResource("sonia/scm/repository/hg.bat.".concat(number));
|
||||
File file = tempFolder.newFile(number);
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(file))
|
||||
{
|
||||
Resources.copy(url, fos);
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param file
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private File testModify(File file)
|
||||
{
|
||||
HgWindowsPackageFix.fixHgPy(file);
|
||||
assertTrue(HgWindowsPackageFix.isSetBinaryAvailable(file));
|
||||
|
||||
File mod = new File(file.getParentFile(), HgWindowsPackageFix.MODIFY_MARK_01);
|
||||
|
||||
assertTrue(mod.exists());
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
@Rule
|
||||
public TemporaryFolder tempFolder = new TemporaryFolder();
|
||||
}
|
||||
@@ -24,114 +24,41 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.HgVersion;
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
import sonia.scm.repository.HgTestUtil;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class HgVersionCommandTest {
|
||||
|
||||
private static final String PYTHON_OUTPUT = String.join("\n",
|
||||
"3.9.0 (default, Oct 27 2020, 14:15:17)",
|
||||
"[Clang 12.0.0 (clang-1200.0.32.21)]"
|
||||
);
|
||||
|
||||
private Map<String, Process> outputs;
|
||||
|
||||
@BeforeEach
|
||||
void setUpOutputs() {
|
||||
outputs = new HashMap<>();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnHgVersion() throws InterruptedException {
|
||||
command("/usr/local/bin/hg", HgVersionCommand.HG_ARGS, "5.5.2", 0);
|
||||
command("/opt/python/bin/python", HgVersionCommand.PYTHON_ARGS, PYTHON_OUTPUT, 0);
|
||||
void shouldReturnVersion(@TempDir Path temp) {
|
||||
HgRepositoryHandler handler = HgTestUtil.createHandler(temp.toFile());
|
||||
HgTestUtil.checkForSkip(handler);
|
||||
|
||||
HgVersion hgVersion = getVersion("/usr/local/bin/hg", "/opt/python/bin/python");
|
||||
assertThat(hgVersion.getMercurial()).isEqualTo("5.5.2");
|
||||
assertThat(hgVersion.getPython()).isEqualTo("3.9.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnUnknownMercurialVersionOnNonZeroExitCode() throws InterruptedException {
|
||||
command("hg", HgVersionCommand.HG_ARGS, "", 1);
|
||||
command("python", HgVersionCommand.PYTHON_ARGS, PYTHON_OUTPUT, 0);
|
||||
|
||||
HgVersion hgVersion = getVersion("hg", "python");
|
||||
assertThat(hgVersion.getMercurial()).isEqualTo(HgVersion.UNKNOWN);
|
||||
assertThat(hgVersion.getPython()).isEqualTo("3.9.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnUnknownPythonVersionOnNonZeroExitCode() throws InterruptedException {
|
||||
command("hg", HgVersionCommand.HG_ARGS, "4.4.2", 0);
|
||||
command("python", HgVersionCommand.PYTHON_ARGS, "", 1);
|
||||
|
||||
HgVersion hgVersion = getVersion("hg", "python");
|
||||
assertThat(hgVersion.getMercurial()).isEqualTo("4.4.2");
|
||||
assertThat(hgVersion.getPython()).isEqualTo(HgVersion.UNKNOWN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnUnknownForInvalidPythonOutput() throws InterruptedException {
|
||||
command("hg", HgVersionCommand.HG_ARGS, "1.0.0", 0);
|
||||
command("python", HgVersionCommand.PYTHON_ARGS, "abcdef", 0);
|
||||
|
||||
HgVersion hgVersion = getVersion("hg", "python");
|
||||
assertThat(hgVersion.getMercurial()).isEqualTo("1.0.0");
|
||||
assertThat(hgVersion.getPython()).isEqualTo(HgVersion.UNKNOWN);
|
||||
HgVersionCommand command = new HgVersionCommand(handler.getConfig());
|
||||
assertThat(command.get())
|
||||
.contains("python/")
|
||||
.contains("mercurial/")
|
||||
.isNotEqualTo(HgVersionCommand.UNKNOWN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnUnknownForIOException() {
|
||||
HgVersionCommand command = new HgVersionCommand(new HgConfig(), cmd -> {
|
||||
HgVersionCommand command = new HgVersionCommand(new HgConfig(), "/i/dont/know", cmd -> {
|
||||
throw new IOException("failed");
|
||||
});
|
||||
|
||||
HgVersion hgVersion = command.get();
|
||||
assertThat(hgVersion.getMercurial()).isEqualTo(HgVersion.UNKNOWN);
|
||||
assertThat(hgVersion.getPython()).isEqualTo(HgVersion.UNKNOWN);
|
||||
}
|
||||
|
||||
private Process command(String command, String[] args, String content, int exitValue) throws InterruptedException {
|
||||
Process process = mock(Process.class);
|
||||
when(process.getInputStream()).thenReturn(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
|
||||
when(process.waitFor()).thenReturn(exitValue);
|
||||
|
||||
List<String> cmdLine = new ArrayList<>();
|
||||
cmdLine.add(command);
|
||||
cmdLine.addAll(Arrays.asList(args));
|
||||
|
||||
outputs.put(Joiner.on(' ').join(cmdLine), process);
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private HgVersion getVersion(String hg, String python) {
|
||||
HgConfig config = new HgConfig();
|
||||
config.setHgBinary(hg);
|
||||
config.setPythonBinary(python);
|
||||
return new HgVersionCommand(config, command -> outputs.get(Joiner.on(' ').join(command))).get();
|
||||
assertThat(command.get()).isEqualTo(HgVersionCommand.UNKNOWN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,12 +21,13 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.web.cgi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
@@ -48,7 +49,9 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
@@ -110,18 +113,8 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param cmd
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void execute(String cmd) throws IOException
|
||||
public void execute(String cmd)
|
||||
{
|
||||
File command = new File(cmd);
|
||||
EnvList env = new EnvList(environment);
|
||||
@@ -150,21 +143,16 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
|
||||
|
||||
env.set(ENV_PATH_TRANSLATED, pathTranslated);
|
||||
|
||||
String execCmd = path;
|
||||
|
||||
if ((execCmd.charAt(0) != '"') && (execCmd.indexOf(' ') >= 0))
|
||||
{
|
||||
execCmd = "\"".concat(execCmd).concat("\"");
|
||||
}
|
||||
|
||||
if (interpreter != null)
|
||||
{
|
||||
execCmd = interpreter.concat(" ").concat(execCmd);
|
||||
List<String> execCmd = new ArrayList<>();
|
||||
if (interpreter != null) {
|
||||
execCmd.add(interpreter);
|
||||
}
|
||||
execCmd.add(command.getAbsolutePath());
|
||||
execCmd.addAll(getArgs());
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("execute cgi: {}", execCmd);
|
||||
logger.debug("execute cgi: {}", Joiner.on(' ').join(execCmd));
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
@@ -173,10 +161,11 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
|
||||
}
|
||||
|
||||
Process p = null;
|
||||
|
||||
try
|
||||
{
|
||||
p = Runtime.getRuntime().exec(execCmd, env.getEnvArray(), workDirectory);
|
||||
try {
|
||||
ProcessBuilder builder = new ProcessBuilder(execCmd);
|
||||
builder.directory(workDirectory);
|
||||
builder.environment().putAll(env.asMap());
|
||||
p = builder.start();
|
||||
execute(p);
|
||||
}
|
||||
catch (IOException ex)
|
||||
@@ -329,15 +318,14 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
|
||||
env.set(ENV_SERVER_SOFTWARE,
|
||||
SERVER_SOFTWARE_PREFIX.concat(SCMContext.getContext().getVersion()));
|
||||
|
||||
Enumeration enm = request.getHeaderNames();
|
||||
Enumeration<String> enm = request.getHeaderNames();
|
||||
|
||||
while (enm.hasMoreElements())
|
||||
{
|
||||
String name = (String) enm.nextElement();
|
||||
String name = enm.nextElement();
|
||||
String value = request.getHeader(name);
|
||||
|
||||
env.set(ENV_HTTP_HEADER_PREFIX + name.toUpperCase().replace('-', '_'),
|
||||
value);
|
||||
env.set(ENV_HTTP_HEADER_PREFIX + name.toUpperCase().replace('-', '_'), value);
|
||||
}
|
||||
|
||||
// these extra ones were from printenv on www.dev.nomura.co.uk
|
||||
@@ -394,6 +382,7 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
private void execute(Process process) throws IOException
|
||||
{
|
||||
InputStream processIS = null;
|
||||
@@ -418,61 +407,26 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param is
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void parseHeaders(InputStream is) throws IOException
|
||||
{
|
||||
private void parseHeaders(InputStream is) throws IOException {
|
||||
String line = null;
|
||||
|
||||
while ((line = getTextLineFromStream(is)).length() > 0)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
while ((line = getTextLineFromStream(is)).length() > 0) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(" ".concat(line));
|
||||
}
|
||||
|
||||
if (!line.startsWith(RESPONSE_HEADER_HTTP_PREFIX))
|
||||
{
|
||||
if (!line.startsWith(RESPONSE_HEADER_HTTP_PREFIX)) {
|
||||
int k = line.indexOf(':');
|
||||
|
||||
if (k > 0)
|
||||
{
|
||||
if (k > 0) {
|
||||
String key = line.substring(0, k).trim();
|
||||
String value = line.substring(k + 1).trim();
|
||||
|
||||
if (RESPONSE_HEADER_LOCATION.equalsIgnoreCase(key))
|
||||
{
|
||||
if (RESPONSE_HEADER_LOCATION.equalsIgnoreCase(key)) {
|
||||
response.sendRedirect(response.encodeRedirectURL(value));
|
||||
}
|
||||
else if (RESPONSE_HEADER_STATUS.equalsIgnoreCase(key))
|
||||
{
|
||||
String[] token = value.split(" ");
|
||||
int status = Integer.parseInt(token[0]);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("CGI returned with status {}", status);
|
||||
}
|
||||
|
||||
if (status < 304)
|
||||
{
|
||||
response.setStatus(status);
|
||||
}
|
||||
else
|
||||
{
|
||||
response.sendError(status);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
} else if (RESPONSE_HEADER_STATUS.equalsIgnoreCase(key)) {
|
||||
handleStatus(value);
|
||||
} else {
|
||||
// add remaining header items to our response header
|
||||
response.addHeader(key, value);
|
||||
}
|
||||
@@ -481,6 +435,19 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
|
||||
}
|
||||
}
|
||||
|
||||
private void handleStatus(String value) throws IOException {
|
||||
String[] token = value.split(" ");
|
||||
int status = Integer.parseInt(token[0]);
|
||||
|
||||
logger.debug("CGI returned with status {}", status);
|
||||
|
||||
if (status < 304) {
|
||||
response.setStatus(status);
|
||||
} else {
|
||||
response.sendError(status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
@@ -513,26 +480,21 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
|
||||
*/
|
||||
private void processErrorStreamAsync(final Process process)
|
||||
{
|
||||
executor.execute(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
InputStream errorStream = null;
|
||||
executor.execute(() -> {
|
||||
InputStream errorStream = null;
|
||||
|
||||
try
|
||||
{
|
||||
errorStream = process.getErrorStream();
|
||||
processErrorStream(errorStream);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.error("could not read errorstream", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(errorStream);
|
||||
}
|
||||
try
|
||||
{
|
||||
errorStream = process.getErrorStream();
|
||||
processErrorStream(errorStream);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.error("could not read errorstream", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(errorStream);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -615,6 +577,7 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
|
||||
catch (InterruptedException ex)
|
||||
{
|
||||
getExceptionHandler().handleException(request, response, ex);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user