mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-02 11:35:57 +01:00
merge with develop
This commit is contained in:
@@ -7,8 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## Unreleased
|
||||
### Added
|
||||
- Rename repository name (and namespace if permitted) ([#1218](https://github.com/scm-manager/scm-manager/pull/1218))
|
||||
- enrich commit mentions in markdown viewer by internal links ([#1210](https://github.com/scm-manager/scm-manager/pull/1210))
|
||||
- New extension point `changeset.description.tokens` to "enrich" commit messages ([#1231](https://github.com/scm-manager/scm-manager/pull/1231))
|
||||
- restart service after rpm or deb package upgrade
|
||||
|
||||
### Changed
|
||||
- Checkboxes can now be 'indeterminate' ([#1215](https://github.com/scm-manager/scm-manager/pull/1215))
|
||||
@@ -16,6 +18,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
- Fixed installation of debian packages on distros without preinstalled `at` ([#1216](https://github.com/scm-manager/scm-manager/issues/1216) and [#1217](https://github.com/scm-manager/scm-manager/pull/1217))
|
||||
- Fixed restart with deb or rpm installation ([#1222](https://github.com/scm-manager/scm-manager/issues/1222) and [#1227](https://github.com/scm-manager/scm-manager/pull/1227))
|
||||
- Fixed broken migration with empty security.xml ([#1219](https://github.com/scm-manager/scm-manager/issues/1219) and [#1221](https://github.com/scm-manager/scm-manager/pull/1221))
|
||||
- Added missing architecture to debian installation documentation ([#1230](https://github.com/scm-manager/scm-manager/pull/1230))
|
||||
- Fixed wrong package information for deb and rpm packages ([#1229](https://github.com/scm-manager/scm-manager/pull/1229))
|
||||
|
||||
|
||||
## [2.1.1] - 2020-06-23
|
||||
### Fixed
|
||||
|
||||
34
README.md
34
README.md
@@ -5,12 +5,12 @@
|
||||
</p>
|
||||
|
||||
The easiest way to share and manage your Git, Mercurial and Subversion
|
||||
repositories over http.
|
||||
repositories.
|
||||
|
||||
- Very easy installation
|
||||
- No need to hack configuration files, SCM-Manager is completely
|
||||
configureable from its Web-Interface
|
||||
- No Apache and no database installation is required
|
||||
configurable from its Web-Interface
|
||||
- No Apache and no database installation required
|
||||
- Central user, group and permission management
|
||||
- Out of the box support for Git, Mercurial and Subversion
|
||||
- Full RESTFul Web Service API (JSON and XML)
|
||||
@@ -19,34 +19,32 @@ repositories over http.
|
||||
- Useful plugins available
|
||||
- Licensed under the MIT-License
|
||||
|
||||
This branch (default) is for the development of SCM-Manager 2.x. If you are interested in the development of version 1.x, please checkout the 1.x branch.
|
||||
This branch (`develop`) is for the development of SCM-Manager 2.x. If you are interested in the development of version
|
||||
1.x, please checkout the branch `support/1.x`.
|
||||
|
||||
## News
|
||||
|
||||
- **2018-09-25** - [SCM-Manager 2 gets a boost by Cloudogu
|
||||
GmbH](https://www.scm-manager.org/scm-manager-2/scm-manager-2-gets-a-boost-by-cloudogu-gmbh/)
|
||||
- **2018-05-04** - SCM-Manager 1.60 released
|
||||
([download](http://www.scm-manager.org/download/) \|
|
||||
[release notes](release-notes.md))
|
||||
- **2018-04-11** - SCM-Manager 1.59 released
|
||||
|
||||
[All news](http://www.scm-manager.org/news/)
|
||||
All news regarding SCM-Manager will be published in our [blog](https://www.scm-manager.org/blog/).
|
||||
|
||||
## Mailing List
|
||||
|
||||
- <scmmanager@googlegroups.com> -
|
||||
[archive](http://groups.google.com/group/scmmanager) \|
|
||||
[subscribe](mailto:scmmanager+subscribe@googlegroups.com)
|
||||
\|
|
||||
[subscribe](mailto:scmmanager+subscribe@googlegroups.com) \|
|
||||
[unsubscribe](mailto:scmmanager+unsubscribe@googlegroups.com)
|
||||
|
||||
## Documentation
|
||||
You can find the complete documentation in the [docs/](docs/Home.md) directory.
|
||||
You can find the complete documentation on our [homepage](https://www.scm-manager.org/docs/).
|
||||
|
||||
## Need help?
|
||||
|
||||
Looking for more guidance? Full documentation lives [in the SCM-Manager repository](https://github.com/scm-manager/scm-manager/blob/develop/docs/Home.md). Do you have further ideas or need support?
|
||||
Looking for more guidance? Full documentation lives on our [homepage](https://www.scm-manager.org/docs/) or the
|
||||
dedicated pages for our [plugins](https://www.scm-manager.org/plugins/). Do you have further ideas or need support?
|
||||
|
||||
- **Community Support** - Contact the SCM-Manager support team for questions about SCM-Manager, to report bugs or to request features through the official channels. [Find more about this here](https://www.scm-manager.org/support/).
|
||||
- **Community Support** - Contact the SCM-Manager support team for questions about SCM-Manager, to report bugs or to
|
||||
request features through the official channels. [Find more about this here](https://www.scm-manager.org/support/).
|
||||
|
||||
- **Enterprise Support** - Do you require support with the integration of SCM-Manager into your processes, with the customization of the tool or simply a service level agreement (SLA)? **Contact our development partner Cloudogu! Their team is looking forward to discussing your individual requirements with you and will be more than happy to give you a quote.** [Request Enterprise Support](https://cloudogu.com/en/scm-manager-enterprise/).
|
||||
- **Enterprise Support** - Do you require support with the integration of SCM-Manager into your processes, with the
|
||||
customization of the tool or simply a service level agreement (SLA)? **Contact our development partner Cloudogu!
|
||||
Their team is looking forward to discussing your individual requirements with you and will be more than happy to
|
||||
give you a quote.** [Request Enterprise Support](https://cloudogu.com/en/scm-manager-enterprise/).
|
||||
|
||||
@@ -9,7 +9,7 @@ displayToc: true
|
||||
The following code block will configure an apt repository for scm-manager and install it.
|
||||
|
||||
```bash
|
||||
echo 'deb https://packages.scm-manager.org/repository/apt-v2-releases/ stable main' | sudo tee /etc/apt/sources.list.d/scm-manager.list
|
||||
echo 'deb [arch=all] https://packages.scm-manager.org/repository/apt-v2-releases/ stable main' | sudo tee /etc/apt/sources.list.d/scm-manager.list
|
||||
sudo apt-key adv --recv-keys --keyserver hkps://keys.openpgp.org 0x975922F193B07D6E
|
||||
sudo apt-get update
|
||||
sudo apt-get install scm-server
|
||||
@@ -24,7 +24,7 @@ To install SCM-Manager as a debian package (.deb), we have to configure an apt r
|
||||
Create a file at `/etc/apt/sources.list.d/scm-manager.list` with the following content:
|
||||
|
||||
```text
|
||||
deb https://packages.scm-manager.org/repository/apt-v2-releases/ stable main
|
||||
deb [arch=all] https://packages.scm-manager.org/repository/apt-v2-releases/ stable main
|
||||
```
|
||||
|
||||
This will add the apt repository of the scm-manager stable releases to the list of your apt repositories.
|
||||
|
||||
35
pom.xml
35
pom.xml
@@ -35,12 +35,17 @@
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<description>
|
||||
The easiest way to share your Git, Mercurial
|
||||
and Subversion repositories over http.
|
||||
and Subversion repositories.
|
||||
</description>
|
||||
<name>scm</name>
|
||||
|
||||
<url>https://github.com/scm-manager/scm-manager</url>
|
||||
|
||||
<organization>
|
||||
<name>Cloudogu GmbH</name>
|
||||
<url>https://cloudogu.com</url>
|
||||
</organization>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>MIT License</name>
|
||||
@@ -50,9 +55,33 @@
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>sdorra</id>
|
||||
<id>sebastian.sdorra</id>
|
||||
<name>Sebastian Sdorra</name>
|
||||
<email>s.sdorra@gmail.com</email>
|
||||
<email>sebastian.sdorra@cloudogu.com</email>
|
||||
<timezone>Europe/Berlin</timezone>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>rene.pfeuffer</id>
|
||||
<name>Rene Pfeufer</name>
|
||||
<email>rene.pfeuffer@cloudogu.com</email>
|
||||
<timezone>Europe/Berlin</timezone>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>eduard.heimbuch</id>
|
||||
<name>Eduard Heimbuch</name>
|
||||
<email>eduard.heimbuch@cloudogu.com</email>
|
||||
<timezone>Europe/Berlin</timezone>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>florian.scholdei</id>
|
||||
<name>Florian Scholdei</name>
|
||||
<email>florian.scholdei@cloudogu.com</email>
|
||||
<timezone>Europe/Berlin</timezone>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>Konstantin Schaper</id>
|
||||
<name>Konstantin Schaper</name>
|
||||
<email>konstantin.schaper@cloudogu.com</email>
|
||||
<timezone>Europe/Berlin</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 sonia.scm.BadRequestException;
|
||||
import sonia.scm.ContextEntry;
|
||||
|
||||
@SuppressWarnings("squid:MaximumInheritanceDepth") // exceptions have a deep inheritance depth themselves; therefore we accept this here
|
||||
public class ChangeNamespaceNotAllowedException extends BadRequestException {
|
||||
|
||||
public ChangeNamespaceNotAllowedException(Repository repository) {
|
||||
super(ContextEntry.ContextBuilder.entity(repository).build(), "change of namespace is not allowed in current namespace strategy");
|
||||
}
|
||||
|
||||
private static final String CODE = "ERS2vYb7U1";
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return CODE;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
import sonia.scm.plugin.ExtensionPoint;
|
||||
@@ -36,8 +36,16 @@ public interface NamespaceStrategy {
|
||||
* Create new namespace for the given repository.
|
||||
*
|
||||
* @param repository repository
|
||||
*
|
||||
* @return namespace
|
||||
*/
|
||||
String createNamespace(Repository repository);
|
||||
|
||||
/**
|
||||
* Checks if the namespace can be changed when using this namespace strategy
|
||||
*
|
||||
* @return namespace can be changed
|
||||
*/
|
||||
default boolean canBeChanged() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ import java.util.Set;
|
||||
*/
|
||||
@StaticPermissions(
|
||||
value = "repository",
|
||||
permissions = {"read", "modify", "delete", "healthCheck", "pull", "push", "permissionRead", "permissionWrite"},
|
||||
permissions = {"read", "modify", "delete", "rename", "healthCheck", "pull", "push", "permissionRead", "permissionWrite"},
|
||||
custom = true, customGlobal = true
|
||||
)
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -38,18 +38,15 @@ import java.util.Collection;
|
||||
* This class is a singleton and is available via injection.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*
|
||||
* @apiviz.uses sonia.scm.repository.RepositoryHandler
|
||||
*/
|
||||
public interface RepositoryManager
|
||||
extends TypeManager<Repository, RepositoryHandler>
|
||||
{
|
||||
extends TypeManager<Repository, RepositoryHandler> {
|
||||
|
||||
/**
|
||||
* Fire {@link RepositoryHookEvent} to the event bus.
|
||||
*
|
||||
* @param event hook event
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public void fireHookEvent(RepositoryHookEvent event);
|
||||
@@ -58,9 +55,7 @@ public interface RepositoryManager
|
||||
* Imports an existing {@link Repository}.
|
||||
* Note: This method should only be called from a {@link RepositoryHandler}.
|
||||
*
|
||||
*
|
||||
* @param repository {@link Repository} to import
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void importRepository(Repository repository) throws IOException;
|
||||
@@ -71,10 +66,7 @@ public interface RepositoryManager
|
||||
* Returns a {@link Repository} by its namespace and name or
|
||||
* null if the {@link Repository} could not be found.
|
||||
*
|
||||
*
|
||||
* @param namespaceAndName namespace and name of the {@link Repository}
|
||||
*
|
||||
*
|
||||
* @return {@link Repository} by its namespace and name or null
|
||||
* if the {@link Repository} could not be found
|
||||
*/
|
||||
@@ -83,7 +75,6 @@ public interface RepositoryManager
|
||||
/**
|
||||
* Returns all configured repository types.
|
||||
*
|
||||
*
|
||||
* @return all configured repository types
|
||||
*/
|
||||
public Collection<RepositoryType> getConfiguredTypes();
|
||||
@@ -91,11 +82,17 @@ public interface RepositoryManager
|
||||
/**
|
||||
* Returns a {@link RepositoryHandler} by the given type (hg, git, svn ...).
|
||||
*
|
||||
*
|
||||
* @param type the type of the {@link RepositoryHandler}
|
||||
*
|
||||
* @return {@link RepositoryHandler} by the given type
|
||||
*/
|
||||
@Override
|
||||
public RepositoryHandler getHandler(String type);
|
||||
|
||||
/**
|
||||
* @param repository the repository {@link Repository}
|
||||
* @param newNameSpace the new repository namespace
|
||||
* @param newName the new repository name
|
||||
* @return {@link Repository} the renamed repository
|
||||
*/
|
||||
public Repository rename(Repository repository, String newNameSpace, String newName);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -42,17 +42,14 @@ import java.util.Collection;
|
||||
*/
|
||||
public class RepositoryManagerDecorator
|
||||
extends ManagerDecorator<Repository>
|
||||
implements RepositoryManager
|
||||
{
|
||||
implements RepositoryManager {
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param decorated
|
||||
*/
|
||||
public RepositoryManagerDecorator(RepositoryManager decorated)
|
||||
{
|
||||
public RepositoryManagerDecorator(RepositoryManager decorated) {
|
||||
super(decorated);
|
||||
this.decorated = decorated;
|
||||
}
|
||||
@@ -63,8 +60,7 @@ public class RepositoryManagerDecorator
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void fireHookEvent(RepositoryHookEvent event)
|
||||
{
|
||||
public void fireHookEvent(RepositoryHookEvent event) {
|
||||
decorated.fireHookEvent(event);
|
||||
}
|
||||
|
||||
@@ -79,65 +75,66 @@ public class RepositoryManagerDecorator
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public Repository get(NamespaceAndName namespaceAndName)
|
||||
{
|
||||
public Repository get(NamespaceAndName namespaceAndName) {
|
||||
return decorated.get(namespaceAndName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Collection<RepositoryType> getConfiguredTypes()
|
||||
{
|
||||
public Collection<RepositoryType> getConfiguredTypes() {
|
||||
return decorated.getConfiguredTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the decorated {@link RepositoryManager}.
|
||||
*
|
||||
*
|
||||
* @return decorated {@link RepositoryManager}
|
||||
*
|
||||
* @since 1.34
|
||||
*/
|
||||
public RepositoryManager getDecorated()
|
||||
{
|
||||
public RepositoryManager getDecorated() {
|
||||
return decorated;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public RepositoryHandler getHandler(String type)
|
||||
{
|
||||
public RepositoryHandler getHandler(String type) {
|
||||
return decorated.getHandler(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Collection<Type> getTypes() {
|
||||
return decorated.getTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Collection<Type> getTypes()
|
||||
{
|
||||
return decorated.getTypes();
|
||||
public Repository rename(Repository repository, String newNamespace, String newName) {
|
||||
return decorated.rename(repository, newNamespace, newName);
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
/**
|
||||
* Field description
|
||||
*/
|
||||
private final RepositoryManager decorated;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public final class RepositoryModificationEvent extends RepositoryEvent implement
|
||||
*/
|
||||
public RepositoryModificationEvent(HandlerEventType eventType, Repository item, Repository itemBeforeModification)
|
||||
{
|
||||
super(eventType, item);
|
||||
super(eventType, item, itemBeforeModification);
|
||||
this.itemBeforeModification = itemBeforeModification;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,30 +73,6 @@
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-jsvc</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>commons-daemon</groupId>
|
||||
<artifactId>commons-daemon-native</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<type>tar.gz</type>
|
||||
<fileMappers>
|
||||
<org.codehaus.plexus.components.io.filemappers.FlattenFileMapper/>
|
||||
</fileMappers>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<includes>**/jsvc-linux-*</includes>
|
||||
<outputDirectory>${project.build.directory}/deb/libexec</outputDirectory>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>true</overWriteSnapshots>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-webapp</id>
|
||||
<phase>prepare-package</phase>
|
||||
@@ -157,7 +133,6 @@
|
||||
</mapper>
|
||||
</data>
|
||||
|
||||
|
||||
<data>
|
||||
<type>file</type>
|
||||
<src>src/main/fs/etc/scm/logging.xml</src>
|
||||
@@ -208,18 +183,6 @@
|
||||
</mapper>
|
||||
</data>
|
||||
|
||||
<data>
|
||||
<type>directory</type>
|
||||
<src>${project.build.directory}/deb/libexec</src>
|
||||
<mapper>
|
||||
<type>perm</type>
|
||||
<prefix>/opt/scm-server/libexec</prefix>
|
||||
<user>root</user>
|
||||
<group>scm</group>
|
||||
<filemode>0755</filemode>
|
||||
</mapper>
|
||||
</data>
|
||||
|
||||
<data>
|
||||
<type>file</type>
|
||||
<src>src/main/fs/opt/scm-server/var/webapp/docroot/index.html</src>
|
||||
|
||||
@@ -18,58 +18,14 @@
|
||||
# Copyright (c) 2001-2002 The Apache Software Foundation. All rights
|
||||
# reserved.
|
||||
|
||||
# user used to run the daemon (defaults to current user)
|
||||
USER="scm"
|
||||
|
||||
# extra jvm arguments
|
||||
EXTRA_JVM_ARGUMENTS="-Djava.awt.headless=true -Dlogback.configurationFile=logging.xml"
|
||||
|
||||
BASEDIR="/opt/scm-server"
|
||||
|
||||
# set pid path for jsvc
|
||||
PIDFILE="/var/run/scm-server.pid"
|
||||
|
||||
# set log dir for jsvc
|
||||
LOGDIR="/var/log/scm"
|
||||
|
||||
# load settings from defaults directory
|
||||
[ -r /etc/default/scm-server ] && . /etc/default/scm-server
|
||||
|
||||
OS=`uname | tr '[:upper:]' '[:lower:]'`
|
||||
ARCH=`uname -m`
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
case "$OS" in
|
||||
sunos*) OS="solaris"
|
||||
ARCH=`uname -p`
|
||||
;;
|
||||
cygwin*) cygwin=true ;;
|
||||
darwin*) darwin=true
|
||||
if [ -z "$JAVA_VERSION" ] ; then
|
||||
JAVA_VERSION="CurrentJDK"
|
||||
else
|
||||
echo "Using Java version: $JAVA_VERSION"
|
||||
fi
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# If a specific java binary isn't specified search for the standard 'java' binary
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
@@ -97,96 +53,12 @@ fi
|
||||
|
||||
CLASSPATH=$CLASSPATH_PREFIX:"$BASEDIR"/conf:"$REPO"/*
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$HOME" ] && HOME=`cygpath --path --windows "$HOME"`
|
||||
[ -n "$BASEDIR" ] && BASEDIR=`cygpath --path --windows "$BASEDIR"`
|
||||
[ -n "$REPO" ] && REPO=`cygpath --path --windows "$REPO"`
|
||||
fi
|
||||
|
||||
jsvc=false;
|
||||
stop="";
|
||||
|
||||
if [ "$1" == "start" ]
|
||||
then
|
||||
jsvc=true;
|
||||
else
|
||||
if [ "$1" == "stop" ]
|
||||
then
|
||||
jsvc=true;
|
||||
stop='-stop';
|
||||
fi
|
||||
fi
|
||||
|
||||
USER_ARGUMENT=""
|
||||
|
||||
if [ "x$USER" != "x" ]
|
||||
then
|
||||
USER_ARGUMENT="-user $USER"
|
||||
fi
|
||||
|
||||
DARWIN_USE_ARCH="false"
|
||||
|
||||
if $jsvc; then
|
||||
|
||||
JSVCCMD=""
|
||||
if [ "$OS" == "darwin" ]; then
|
||||
if [ "$DARWIN_USE_ARCH" == "true" ]; then
|
||||
JSVCCMD="/usr/bin/arch -arch $ARCH $BASEDIR/libexec/jsvc-$OS"
|
||||
else
|
||||
JSVCCMD="exec $BASEDIR/libexec/jsvc-$OS"
|
||||
fi
|
||||
else
|
||||
JSVCCMD="exec $BASEDIR/libexec/jsvc-$OS-$ARCH"
|
||||
fi
|
||||
|
||||
# try to extract JAVA_HOME from JAVACMD
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
PRG="$JAVACMD"
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
DIR="$(dirname "$PRG")"
|
||||
DIR="$(dirname "$DIR")"
|
||||
if [ -d "$DIR" ] ; then
|
||||
JAVA_HOME="$DIR"
|
||||
fi
|
||||
fi
|
||||
|
||||
# TODO JVM Arguments
|
||||
|
||||
$JSVCCMD -cp "$CLASSPATH" $JAVA_OPTS \
|
||||
$EXTRA_JVM_ARGUMENTS $USER_ARGUMENT \
|
||||
-outfile "$LOGDIR/scm-server.out" \
|
||||
-errfile "$LOGDIR/scm-server.err" \
|
||||
-pidfile "$PIDFILE" \
|
||||
-jvm server \
|
||||
-home "$JAVA_HOME" \
|
||||
-Dapp.name="scm-server" \
|
||||
-Dapp.pid="$$" \
|
||||
-Dapp.repo="$REPO" \
|
||||
-Dbasedir="$BASEDIR" \
|
||||
$stop sonia.scm.server.ScmServerDaemon \
|
||||
"$@"
|
||||
|
||||
else
|
||||
|
||||
exec "$JAVACMD" $JAVA_OPTS \
|
||||
$EXTRA_JVM_ARGUMENTS \
|
||||
-classpath "$CLASSPATH" \
|
||||
-Dapp.name="scm-server" \
|
||||
-Dapp.pid="$$" \
|
||||
-Dapp.repo="$REPO" \
|
||||
-Dbasedir="$BASEDIR" \
|
||||
sonia.scm.server.ScmServerDaemon \
|
||||
"$@"
|
||||
|
||||
fi
|
||||
exec "$JAVACMD" $JAVA_OPTS \
|
||||
$EXTRA_JVM_ARGUMENTS \
|
||||
-classpath "$CLASSPATH" \
|
||||
-Dapp.name="scm-server" \
|
||||
-Dapp.pid="$$" \
|
||||
-Dapp.repo="$REPO" \
|
||||
-Dbasedir="$BASEDIR" \
|
||||
sonia.scm.server.ScmServerDaemon \
|
||||
"$@"
|
||||
|
||||
@@ -3,7 +3,8 @@ Version: [[version]]
|
||||
Section: devel
|
||||
Priority: extra
|
||||
Architecture: all
|
||||
Description: The easiest way to share and manage your Git, Mercurial and Subversion repositories over http
|
||||
Maintainer: Sebastian Sdorra <sebastian.sdorra@cloudogu.com>
|
||||
Description: The easiest way to share and manage your Git, Mercurial and Subversion repositories
|
||||
Maintainer: SCM-Team <scm-team@cloudogu.com>
|
||||
Homepage: https://scm-manager.org
|
||||
Depends: adduser, procps, psmisc, net-tools
|
||||
Recommends: openjdk-11-jre-headless, mercurial
|
||||
|
||||
@@ -37,7 +37,22 @@ systemctl daemon-reload
|
||||
# enable and start the service
|
||||
sudo systemctl enable scm-server
|
||||
|
||||
# we start scm-manager after 5 seconds
|
||||
# this is required, because if we install scm-manager with recommend java
|
||||
# java is not fully setup if we ran our postint script
|
||||
nohup sh -c "sleep 5; systemctl start scm-server" >/dev/null 2>&1 &
|
||||
# reload systemd and make service available
|
||||
systemctl --system daemon-reload || true
|
||||
|
||||
# enable service
|
||||
if ! systemctl is-enabled scm-server >/dev/null
|
||||
then
|
||||
systemctl enable scm-server
|
||||
fi
|
||||
|
||||
# start or restart service
|
||||
if systemctl is-active scm-server >/dev/null
|
||||
then
|
||||
systemctl restart scm-server
|
||||
else
|
||||
# we start scm-manager after 5 seconds
|
||||
# this is required, because if we install scm-manager with recommend java
|
||||
# java is not fully setup if we ran our postint script
|
||||
nohup sh -c "sleep 5; systemctl start scm-server" >/dev/null 2>&1 &
|
||||
fi
|
||||
|
||||
@@ -29,21 +29,12 @@ HOST=0.0.0.0
|
||||
# scm-server port
|
||||
PORT=8080
|
||||
|
||||
# change user
|
||||
USER=scm
|
||||
|
||||
# home of scm-manager
|
||||
export SCM_HOME=/var/lib/scm
|
||||
|
||||
# force jvm path
|
||||
# JAVA_HOME="/usr/lib/jvm/java-11-openjdk-amd64"
|
||||
|
||||
# path to pid
|
||||
PIDFILE=/var/run/scm/scm.pid
|
||||
|
||||
# path to log directory
|
||||
LOGDIR=/var/log/scm
|
||||
|
||||
# increase memory
|
||||
# EXTRA_JVM_ARGUMENTS="$EXTRA_JVM_ARGUMENTS -Xms1g -Xmx1g"
|
||||
|
||||
|
||||
@@ -100,7 +100,8 @@
|
||||
|
||||
<root level="WARN">
|
||||
<appender-ref ref="FILE" />
|
||||
<appender-ref ref="STDOUT" />
|
||||
<!-- uncomment to enable logging to stdout -->
|
||||
<!-- appender-ref ref="STDOUT" /-->
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
|
||||
@@ -3,14 +3,19 @@ Description=SCM-Manager Server
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
Type=simple
|
||||
|
||||
User=scm
|
||||
Group=scm
|
||||
|
||||
WorkingDirectory=/opt/scm-server
|
||||
ExecStart=/opt/scm-server/bin/scm-server start
|
||||
ExecStop=/opt/scm-server/bin/scm-server stop
|
||||
ExecStart=/opt/scm-server/bin/scm-server
|
||||
Restart=on-failure
|
||||
|
||||
# Exit code 143 means that the program received a SIGTERM signal to instruct it to exit,
|
||||
# but it did not handle the signal properly.
|
||||
# we suppress that warning for now
|
||||
SuccessExitStatus=143
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -41,6 +41,12 @@
|
||||
<description>Packaging for RedHat/Centos/Fedora</description>
|
||||
<name>rpm</name>
|
||||
|
||||
<!--
|
||||
de.dentrassi.maven:rpm
|
||||
has no other way to set the url of the package
|
||||
-->
|
||||
<url>https://scm-manager.org</url>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
@@ -73,30 +79,6 @@
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-jsvc</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>commons-daemon</groupId>
|
||||
<artifactId>commons-daemon-native</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<type>tar.gz</type>
|
||||
<fileMappers>
|
||||
<org.codehaus.plexus.components.io.filemappers.FlattenFileMapper/>
|
||||
</fileMappers>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<includes>**/jsvc-linux-*</includes>
|
||||
<outputDirectory>${project.build.directory}/rpm/libexec</outputDirectory>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>true</overWriteSnapshots>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-webapp</id>
|
||||
<phase>prepare-package</phase>
|
||||
@@ -127,6 +109,8 @@
|
||||
<configuration>
|
||||
<attach>true</attach>
|
||||
<packageName>scm-server</packageName>
|
||||
<summary>SCM-Manager Server</summary>
|
||||
<description>The easiest way to share and manage your Git, Mercurial and Subversion repositories</description>
|
||||
<group>Development/Tools</group>
|
||||
<license>MIT</license>
|
||||
<skipSigning>true</skipSigning>
|
||||
@@ -163,15 +147,6 @@
|
||||
<group>scm</group>
|
||||
<mode>0644</mode>
|
||||
</rule>
|
||||
<rule>
|
||||
<when>
|
||||
<type>file</type>
|
||||
<prefix>/opt/scm-server/libexec</prefix>
|
||||
</when>
|
||||
<user>root</user>
|
||||
<group>scm</group>
|
||||
<mode>0755</mode>
|
||||
</rule>
|
||||
</rules>
|
||||
</ruleset>
|
||||
</rulesets>
|
||||
@@ -229,14 +204,6 @@
|
||||
<ruleset>default</ruleset>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
<name>/opt/scm-server/libexec</name>
|
||||
<collect>
|
||||
<from>${project.build.directory}/rpm/libexec</from>
|
||||
</collect>
|
||||
<ruleset>default</ruleset>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
<name>/opt/scm-server/var/webapp/docroot/index.html</name>
|
||||
<file>src/main/fs/opt/scm-server/var/webapp/docroot/index.html</file>
|
||||
@@ -319,7 +286,6 @@
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
|
||||
@@ -18,58 +18,14 @@
|
||||
# Copyright (c) 2001-2002 The Apache Software Foundation. All rights
|
||||
# reserved.
|
||||
|
||||
# user used to run the daemon (defaults to current user)
|
||||
USER="scm"
|
||||
|
||||
# extra jvm arguments
|
||||
EXTRA_JVM_ARGUMENTS="-Djava.awt.headless=true -Dlogback.configurationFile=logging.xml"
|
||||
|
||||
BASEDIR="/opt/scm-server"
|
||||
|
||||
# set pid path for jsvc
|
||||
PIDFILE="/var/run/scm-server.pid"
|
||||
|
||||
# set log dir for jsvc
|
||||
LOGDIR="/var/log/scm"
|
||||
|
||||
# load settings from defaults directory
|
||||
[ -r /etc/default/scm-server ] && . /etc/default/scm-server
|
||||
|
||||
OS=`uname | tr '[:upper:]' '[:lower:]'`
|
||||
ARCH=`uname -m`
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
case "$OS" in
|
||||
sunos*) OS="solaris"
|
||||
ARCH=`uname -p`
|
||||
;;
|
||||
cygwin*) cygwin=true ;;
|
||||
darwin*) darwin=true
|
||||
if [ -z "$JAVA_VERSION" ] ; then
|
||||
JAVA_VERSION="CurrentJDK"
|
||||
else
|
||||
echo "Using Java version: $JAVA_VERSION"
|
||||
fi
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# If a specific java binary isn't specified search for the standard 'java' binary
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
@@ -97,96 +53,12 @@ fi
|
||||
|
||||
CLASSPATH=$CLASSPATH_PREFIX:"$BASEDIR"/conf:"$REPO"/*
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$HOME" ] && HOME=`cygpath --path --windows "$HOME"`
|
||||
[ -n "$BASEDIR" ] && BASEDIR=`cygpath --path --windows "$BASEDIR"`
|
||||
[ -n "$REPO" ] && REPO=`cygpath --path --windows "$REPO"`
|
||||
fi
|
||||
|
||||
jsvc=false;
|
||||
stop="";
|
||||
|
||||
if [ "$1" == "start" ]
|
||||
then
|
||||
jsvc=true;
|
||||
else
|
||||
if [ "$1" == "stop" ]
|
||||
then
|
||||
jsvc=true;
|
||||
stop='-stop';
|
||||
fi
|
||||
fi
|
||||
|
||||
USER_ARGUMENT=""
|
||||
|
||||
if [ "x$USER" != "x" ]
|
||||
then
|
||||
USER_ARGUMENT="-user $USER"
|
||||
fi
|
||||
|
||||
DARWIN_USE_ARCH="false"
|
||||
|
||||
if $jsvc; then
|
||||
|
||||
JSVCCMD=""
|
||||
if [ "$OS" == "darwin" ]; then
|
||||
if [ "$DARWIN_USE_ARCH" == "true" ]; then
|
||||
JSVCCMD="/usr/bin/arch -arch $ARCH $BASEDIR/libexec/jsvc-$OS"
|
||||
else
|
||||
JSVCCMD="exec $BASEDIR/libexec/jsvc-$OS"
|
||||
fi
|
||||
else
|
||||
JSVCCMD="exec $BASEDIR/libexec/jsvc-$OS-$ARCH"
|
||||
fi
|
||||
|
||||
# try to extract JAVA_HOME from JAVACMD
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
PRG="$JAVACMD"
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
DIR="$(dirname "$PRG")"
|
||||
DIR="$(dirname "$DIR")"
|
||||
if [ -d "$DIR" ] ; then
|
||||
JAVA_HOME="$DIR"
|
||||
fi
|
||||
fi
|
||||
|
||||
# TODO JVM Arguments
|
||||
|
||||
$JSVCCMD -cp "$CLASSPATH" $JAVA_OPTS \
|
||||
$EXTRA_JVM_ARGUMENTS $USER_ARGUMENT \
|
||||
-outfile "$LOGDIR/scm-server.out" \
|
||||
-errfile "$LOGDIR/scm-server.err" \
|
||||
-pidfile "$PIDFILE" \
|
||||
-jvm server \
|
||||
-home "$JAVA_HOME" \
|
||||
-Dapp.name="scm-server" \
|
||||
-Dapp.pid="$$" \
|
||||
-Dapp.repo="$REPO" \
|
||||
-Dbasedir="$BASEDIR" \
|
||||
$stop sonia.scm.server.ScmServerDaemon \
|
||||
"$@"
|
||||
|
||||
else
|
||||
|
||||
exec "$JAVACMD" $JAVA_OPTS \
|
||||
$EXTRA_JVM_ARGUMENTS \
|
||||
-classpath "$CLASSPATH" \
|
||||
-Dapp.name="scm-server" \
|
||||
-Dapp.pid="$$" \
|
||||
-Dapp.repo="$REPO" \
|
||||
-Dbasedir="$BASEDIR" \
|
||||
sonia.scm.server.ScmServerDaemon \
|
||||
"$@"
|
||||
|
||||
fi
|
||||
exec "$JAVACMD" $JAVA_OPTS \
|
||||
$EXTRA_JVM_ARGUMENTS \
|
||||
-classpath "$CLASSPATH" \
|
||||
-Dapp.name="scm-server" \
|
||||
-Dapp.pid="$$" \
|
||||
-Dapp.repo="$REPO" \
|
||||
-Dbasedir="$BASEDIR" \
|
||||
sonia.scm.server.ScmServerDaemon \
|
||||
"$@"
|
||||
|
||||
@@ -29,21 +29,12 @@ HOST=0.0.0.0
|
||||
# scm-server port
|
||||
PORT=8080
|
||||
|
||||
# change user
|
||||
USER=scm
|
||||
|
||||
# home of scm-manager
|
||||
export SCM_HOME=/var/lib/scm
|
||||
|
||||
# force jvm path
|
||||
# JAVA_HOME="/usr/lib/jvm/jre-11"
|
||||
|
||||
# path to pid
|
||||
PIDFILE=/var/run/scm/scm.pid
|
||||
|
||||
# path to log directory
|
||||
LOGDIR=/var/log/scm
|
||||
|
||||
# increase memory
|
||||
# EXTRA_JVM_ARGUMENTS="$EXTRA_JVM_ARGUMENTS -Xms1g -Xmx1g"
|
||||
|
||||
|
||||
@@ -100,7 +100,8 @@
|
||||
|
||||
<root level="WARN">
|
||||
<appender-ref ref="FILE" />
|
||||
<appender-ref ref="STDOUT" />
|
||||
<!-- uncomment to enable logging to stdout -->
|
||||
<!-- appender-ref ref="STDOUT" /-->
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
|
||||
@@ -3,14 +3,19 @@ Description=SCM-Manager Server
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
Type=simple
|
||||
|
||||
User=scm
|
||||
Group=scm
|
||||
|
||||
WorkingDirectory=/opt/scm-server
|
||||
ExecStart=/opt/scm-server/bin/scm-server start
|
||||
ExecStop=/opt/scm-server/bin/scm-server stop
|
||||
ExecStart=/opt/scm-server/bin/scm-server
|
||||
Restart=on-failure
|
||||
|
||||
# Exit code 143 means that the program received a SIGTERM signal to instruct it to exit,
|
||||
# but it did not handle the signal properly.
|
||||
# we suppress that warning for now
|
||||
SuccessExitStatus=143
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -32,8 +32,18 @@ if [ -d "${WORKDIR}" ]; then
|
||||
fi
|
||||
|
||||
# reload systemd and make service available
|
||||
systemctl daemon-reload
|
||||
systemctl --system daemon-reload || true
|
||||
|
||||
# enable and start the service
|
||||
sudo systemctl enable scm-server
|
||||
sudo systemctl start scm-server
|
||||
# enable service
|
||||
if ! systemctl is-enabled scm-server >/dev/null
|
||||
then
|
||||
systemctl enable scm-server
|
||||
fi
|
||||
|
||||
# start or restart service
|
||||
if systemctl is-active scm-server >/dev/null
|
||||
then
|
||||
systemctl restart scm-server
|
||||
else
|
||||
systemctl start scm-server
|
||||
fi
|
||||
|
||||
@@ -113,7 +113,8 @@
|
||||
"repositoryForm": {
|
||||
"subtitle": "Repository bearbeiten",
|
||||
"submit": "Speichern",
|
||||
"initializeRepository": "Repository initiieren"
|
||||
"initializeRepository": "Repository initiieren",
|
||||
"dangerZone": "Gefahrenzone"
|
||||
},
|
||||
"sources": {
|
||||
"file-tree": {
|
||||
@@ -196,6 +197,8 @@
|
||||
},
|
||||
"deleteRepo": {
|
||||
"button": "Repository löschen",
|
||||
"subtitle": "Löscht dieses Repository",
|
||||
"description": "Diese Aktion kann nicht rückgangig gemacht werden.",
|
||||
"confirmAlert": {
|
||||
"title": "Repository löschen",
|
||||
"message": "Soll das Repository wirklich gelöscht werden?",
|
||||
@@ -203,6 +206,22 @@
|
||||
"cancel": "Nein"
|
||||
}
|
||||
},
|
||||
"renameRepo": {
|
||||
"button": "Repository umbenennen",
|
||||
"subtitle": "Benennt dieses Repository um",
|
||||
"description": "Es werden keine Weiterleitung auf den neuen Namen eingerichtet.",
|
||||
"modal": {
|
||||
"title": "Repository umbenennen",
|
||||
"label": {
|
||||
"repoName": "Repository Name",
|
||||
"repoNamespace": "Repository Namespace"
|
||||
},
|
||||
"button": {
|
||||
"rename": "Umbenennen",
|
||||
"cancel": "Abbrechen"
|
||||
}
|
||||
}
|
||||
},
|
||||
"diff": {
|
||||
"sideBySide": "Zur zweispaltigen Ansicht wechseln",
|
||||
"combined": "Zur kombinierten Ansicht wechseln",
|
||||
|
||||
@@ -113,7 +113,8 @@
|
||||
"repositoryForm": {
|
||||
"subtitle": "Edit Repository",
|
||||
"submit": "Save",
|
||||
"initializeRepository": "Initialize repository"
|
||||
"initializeRepository": "Initialize repository",
|
||||
"dangerZone": "Danger Zone"
|
||||
},
|
||||
"sources": {
|
||||
"file-tree": {
|
||||
@@ -196,6 +197,8 @@
|
||||
},
|
||||
"deleteRepo": {
|
||||
"button": "Delete Repository",
|
||||
"subtitle": "Deletes this repository",
|
||||
"description": "Once a repository was deleted, this cannot be undone. Please be careful with this action.",
|
||||
"confirmAlert": {
|
||||
"title": "Delete repository",
|
||||
"message": "Do you really want to delete the repository?",
|
||||
@@ -203,6 +206,22 @@
|
||||
"cancel": "No"
|
||||
}
|
||||
},
|
||||
"renameRepo": {
|
||||
"button": "Rename Repository",
|
||||
"subtitle": "Renames this repository",
|
||||
"description": "There will be no redirects to the renamed repository.",
|
||||
"modal": {
|
||||
"title": "Rename repository",
|
||||
"label": {
|
||||
"repoName": "Repository name",
|
||||
"repoNamespace": "Repository namespace"
|
||||
},
|
||||
"button": {
|
||||
"rename": "Rename",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"diff": {
|
||||
"changes": {
|
||||
"add": "added",
|
||||
|
||||
@@ -44,7 +44,7 @@ import GlobalConfig from "./GlobalConfig";
|
||||
import RepositoryRoles from "../roles/containers/RepositoryRoles";
|
||||
import SingleRepositoryRole from "../roles/containers/SingleRepositoryRole";
|
||||
import CreateRepositoryRole from "../roles/containers/CreateRepositoryRole";
|
||||
import { StateMenuContextProvider } from "@scm-manager/ui-components/src/navigation/MenuContext";
|
||||
import { StateMenuContextProvider } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = RouteComponentProps &
|
||||
WithTranslation & {
|
||||
|
||||
@@ -54,7 +54,7 @@ import PluginBottomActions from "../components/PluginBottomActions";
|
||||
import ExecutePendingActionModal from "../components/ExecutePendingActionModal";
|
||||
import CancelPendingActionModal from "../components/CancelPendingActionModal";
|
||||
import UpdateAllActionModal from "../components/UpdateAllActionModal";
|
||||
import { Plugin } from "@scm-manager/ui-types/src";
|
||||
import { Plugin } from "@scm-manager/ui-types";
|
||||
import ShowPendingModal from "../components/ShowPendingModal";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
|
||||
@@ -31,7 +31,7 @@ import { Page } from "@scm-manager/ui-components";
|
||||
import { getGroupsLink, getUserAutoCompleteLink } from "../../modules/indexResource";
|
||||
import { createGroup, createGroupReset, getCreateGroupFailure, isCreateGroupPending } from "../modules/groups";
|
||||
import GroupForm from "../components/GroupForm";
|
||||
import { apiClient } from "@scm-manager/ui-components/src";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
createGroup: (link: string, group: Group, callback?: () => void) => void;
|
||||
|
||||
@@ -31,7 +31,7 @@ import { DisplayedUser, Group } from "@scm-manager/ui-types";
|
||||
import { ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { getUserAutoCompleteLink } from "../../modules/indexResource";
|
||||
import DeleteGroup from "./DeleteGroup";
|
||||
import { apiClient } from "@scm-manager/ui-components/src";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import { compose } from "redux";
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -26,8 +26,9 @@ import styled from "styled-components";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { Repository, RepositoryType } from "@scm-manager/ui-types";
|
||||
import { Checkbox, Level, InputField, Select, SubmitButton, Subtitle, Textarea } from "@scm-manager/ui-components";
|
||||
import { Checkbox, InputField, Level, Select, SubmitButton, Subtitle, Textarea } from "@scm-manager/ui-components";
|
||||
import * as validator from "./repositoryValidation";
|
||||
import { CUSTOM_NAMESPACE_STRATEGY } from "../../modules/repos";
|
||||
|
||||
const CheckboxWrapper = styled.div`
|
||||
margin-top: 2em;
|
||||
@@ -59,8 +60,6 @@ type State = {
|
||||
contactValidationError: boolean;
|
||||
};
|
||||
|
||||
const CUSTOM_NAMESPACE_STRATEGY = "CustomNamespaceStrategy";
|
||||
|
||||
class RepositoryForm extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
@@ -108,7 +107,7 @@ class RepositoryForm extends React.Component<Props, State> {
|
||||
);
|
||||
};
|
||||
|
||||
submit = (event: Event) => {
|
||||
submit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
if (this.isValid()) {
|
||||
this.props.submitForm(this.state.repository, this.state.initRepository);
|
||||
|
||||
73
scm-ui/ui-webapp/src/repos/containers/DangerZone.tsx
Normal file
73
scm-ui/ui-webapp/src/repos/containers/DangerZone.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React, { FC } from "react";
|
||||
import { Repository, Links } from "@scm-manager/ui-types";
|
||||
import RenameRepository from "./RenameRepository";
|
||||
import DeleteRepo from "./DeleteRepo";
|
||||
import styled from "styled-components";
|
||||
import { Subtitle } from "@scm-manager/ui-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
repository: Repository;
|
||||
indexLinks: Links;
|
||||
};
|
||||
|
||||
const DangerZoneContainer = styled.div`
|
||||
padding: 1rem;
|
||||
border: 1px solid #ff6a88;
|
||||
border-radius: 5px;
|
||||
> *:not(:last-child) {
|
||||
padding-bottom: 1.5rem;
|
||||
border-bottom: solid 2px whitesmoke;
|
||||
}
|
||||
`;
|
||||
|
||||
const DangerZone: FC<Props> = ({ repository, indexLinks }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
|
||||
const dangerZone = [];
|
||||
if (repository?._links?.rename || repository?._links?.renameWithNamespace) {
|
||||
dangerZone.push(<RenameRepository repository={repository} indexLinks={indexLinks} />);
|
||||
}
|
||||
if (repository?._links?.delete) {
|
||||
// @ts-ignore
|
||||
dangerZone.push(<DeleteRepo repository={repository} />);
|
||||
}
|
||||
|
||||
if (dangerZone.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<hr />
|
||||
<Subtitle subtitle={t("repositoryForm.dangerZone")} />
|
||||
<DangerZoneContainer>{dangerZone.map(entry => entry)}</DangerZoneContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DangerZone;
|
||||
@@ -24,23 +24,21 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { compose } from "redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import { RouteComponentProps, withRouter } from "react-router-dom";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { History } from "history";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { confirmAlert, DeleteButton, ErrorNotification, Level } from "@scm-manager/ui-components";
|
||||
import { confirmAlert, DeleteButton, ErrorNotification, Level, ButtonGroup } from "@scm-manager/ui-components";
|
||||
import { deleteRepo, getDeleteRepoFailure, isDeleteRepoPending } from "../modules/repos";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
repository: Repository;
|
||||
confirmDialog?: boolean;
|
||||
deleteRepo: (p1: Repository, p2: () => void) => void;
|
||||
|
||||
// context props
|
||||
history: History;
|
||||
};
|
||||
type Props = RouteComponentProps &
|
||||
WithTranslation & {
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
repository: Repository;
|
||||
confirmDialog?: boolean;
|
||||
deleteRepo: (p1: Repository, p2: () => void) => void;
|
||||
};
|
||||
|
||||
class DeleteRepo extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
@@ -88,9 +86,16 @@ class DeleteRepo extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<>
|
||||
<hr />
|
||||
<ErrorNotification error={error} />
|
||||
<Level right={<DeleteButton label={t("deleteRepo.button")} action={action} loading={loading} />} />
|
||||
<Level
|
||||
left={
|
||||
<div>
|
||||
<strong>{t("deleteRepo.subtitle")}</strong>
|
||||
<p>{t("deleteRepo.description")}</p>
|
||||
</div>
|
||||
}
|
||||
right={<DeleteButton label={t("deleteRepo.button")} action={action} loading={loading} />}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,17 +25,19 @@ import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import RepositoryForm from "../components/form";
|
||||
import DeleteRepo from "./DeleteRepo";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { Repository, Links } from "@scm-manager/ui-types";
|
||||
import { getModifyRepoFailure, isModifyRepoPending, modifyRepo, modifyRepoReset } from "../modules/repos";
|
||||
import { History } from "history";
|
||||
import { ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { compose } from "redux";
|
||||
import DangerZone from "./DangerZone";
|
||||
import { getLinks } from "../../modules/indexResource";
|
||||
|
||||
type Props = {
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
indexLinks: Links;
|
||||
|
||||
modifyRepo: (p1: Repository, p2: () => void) => void;
|
||||
modifyRepoReset: (p: Repository) => void;
|
||||
@@ -69,7 +71,7 @@ class EditRepo extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { loading, error, repository } = this.props;
|
||||
const { loading, error, repository, indexLinks } = this.props;
|
||||
|
||||
const url = this.matchedUrl();
|
||||
|
||||
@@ -79,7 +81,7 @@ class EditRepo extends React.Component<Props> {
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<ErrorNotification error={error} />
|
||||
<RepositoryForm
|
||||
repository={this.props.repository}
|
||||
@@ -89,8 +91,8 @@ class EditRepo extends React.Component<Props> {
|
||||
}}
|
||||
/>
|
||||
<ExtensionPoint name="repo-config.route" props={extensionProps} renderAll={true} />
|
||||
<DeleteRepo repository={repository} />
|
||||
</div>
|
||||
<DangerZone repository={repository} indexLinks={indexLinks} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -99,9 +101,12 @@ const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
const { namespace, name } = ownProps.repository;
|
||||
const loading = isModifyRepoPending(state, namespace, name);
|
||||
const error = getModifyRepoFailure(state, namespace, name);
|
||||
const indexLinks = getLinks(state);
|
||||
|
||||
return {
|
||||
loading,
|
||||
error
|
||||
error,
|
||||
indexLinks
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
178
scm-ui/ui-webapp/src/repos/containers/RenameRepository.tsx
Normal file
178
scm-ui/ui-webapp/src/repos/containers/RenameRepository.tsx
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React, { FC, useEffect, useState } from "react";
|
||||
import { Link, Links, Repository } from "@scm-manager/ui-types";
|
||||
import { CONTENT_TYPE, CUSTOM_NAMESPACE_STRATEGY } from "../modules/repos";
|
||||
import { Button, ButtonGroup, ErrorNotification, InputField, Level, Loading, Modal } from "@scm-manager/ui-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import * as validator from "../components/form/repositoryValidation";
|
||||
|
||||
type Props = {
|
||||
repository: Repository;
|
||||
indexLinks: Links;
|
||||
};
|
||||
|
||||
const RenameRepository: FC<Props> = ({ repository, indexLinks }) => {
|
||||
let history = useHistory();
|
||||
const [t] = useTranslation("repos");
|
||||
const [error, setError] = useState<Error | undefined>(undefined);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [name, setName] = useState(repository.name);
|
||||
const [namespace, setNamespace] = useState(repository.namespace);
|
||||
const [nameValidationError, setNameValidationError] = useState(false);
|
||||
const [namespaceValidationError, setNamespaceValidationError] = useState(false);
|
||||
const [currentNamespaceStrategie, setCurrentNamespaceStrategy] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
apiClient
|
||||
.get((indexLinks?.namespaceStrategies as Link).href)
|
||||
.then(result => result.json())
|
||||
.then(result => setCurrentNamespaceStrategy(result.current))
|
||||
.catch(setError);
|
||||
}, [repository]);
|
||||
|
||||
if (error) {
|
||||
return <ErrorNotification error={error} />;
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
const isValid =
|
||||
!nameValidationError &&
|
||||
!namespaceValidationError &&
|
||||
(repository.name !== name || repository.namespace !== namespace);
|
||||
|
||||
const handleNamespaceChange = (namespace: string) => {
|
||||
setNamespaceValidationError(!validator.isNameValid(namespace));
|
||||
setNamespace(namespace);
|
||||
};
|
||||
|
||||
const handleNameChange = (name: string) => {
|
||||
setNameValidationError(!validator.isNameValid(name));
|
||||
setName(name);
|
||||
};
|
||||
|
||||
const renderNamespaceField = () => {
|
||||
const props = {
|
||||
label: t("repository.namespace"),
|
||||
helpText: t("help.namespaceHelpText"),
|
||||
value: namespace,
|
||||
onChange: handleNamespaceChange,
|
||||
errorMessage: t("validation.namespace-invalid"),
|
||||
validationError: namespaceValidationError
|
||||
};
|
||||
|
||||
if (currentNamespaceStrategie === CUSTOM_NAMESPACE_STRATEGY) {
|
||||
return <InputField {...props} />;
|
||||
}
|
||||
|
||||
return <ExtensionPoint name="repos.create.namespace" props={props} renderAll={false} />;
|
||||
};
|
||||
|
||||
const rename = () => {
|
||||
setLoading(true);
|
||||
const url = repository?._links?.renameWithNamespace
|
||||
? (repository?._links?.renameWithNamespace as Link).href
|
||||
: (repository?._links?.rename as Link).href;
|
||||
|
||||
apiClient
|
||||
.post(url, { name, namespace }, CONTENT_TYPE)
|
||||
.then(() => setLoading(false))
|
||||
.then(() => history.push(`/repo/${namespace}/${name}`))
|
||||
.catch(setError);
|
||||
};
|
||||
|
||||
const modalBody = (
|
||||
<div>
|
||||
<InputField
|
||||
label={t("renameRepo.modal.label.repoName")}
|
||||
name={t("renameRepo.modal.label.repoName")}
|
||||
errorMessage={t("validation.name-invalid")}
|
||||
helpText={t("help.nameHelpText")}
|
||||
validationError={nameValidationError}
|
||||
value={name}
|
||||
onChange={handleNameChange}
|
||||
/>
|
||||
{renderNamespaceField()}
|
||||
</div>
|
||||
);
|
||||
|
||||
const footer = (
|
||||
<>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
color="warning"
|
||||
icon="exclamation-triangle"
|
||||
label={t("renameRepo.modal.button.rename")}
|
||||
disabled={!isValid}
|
||||
title={t("renameRepo.modal.button.rename")}
|
||||
action={rename}
|
||||
/>
|
||||
<Button
|
||||
label={t("renameRepo.modal.button.cancel")}
|
||||
title={t("renameRepo.modal.button.cancel")}
|
||||
action={() => setShowModal(false)}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
active={showModal}
|
||||
title={t("renameRepo.modal.title")}
|
||||
footer={footer}
|
||||
body={modalBody}
|
||||
closeFunction={() => setShowModal(false)}
|
||||
/>
|
||||
<Level
|
||||
left={
|
||||
<div>
|
||||
<strong>{t("renameRepo.subtitle")}</strong>
|
||||
<p>{t("renameRepo.description")}</p>
|
||||
</div>
|
||||
}
|
||||
right={
|
||||
<Button
|
||||
label={t("renameRepo.button")}
|
||||
action={() => setShowModal(true)}
|
||||
loading={loading}
|
||||
color="warning"
|
||||
icon="edit"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RenameRepository;
|
||||
@@ -74,6 +74,13 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
fetchRepoByName(repoLink, namespace, name);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
const { fetchRepoByName, namespace, name, repoLink } = this.props;
|
||||
if (namespace !== prevProps.namespace || name !== prevProps.name) {
|
||||
fetchRepoByName(repoLink, namespace, name);
|
||||
}
|
||||
}
|
||||
|
||||
stripEndingSlash = (url: string) => {
|
||||
if (url.endsWith("/")) {
|
||||
return url.substring(0, url.length - 1);
|
||||
|
||||
@@ -27,7 +27,6 @@ import * as types from "../../modules/types";
|
||||
import { Action, Repository, RepositoryCollection } from "@scm-manager/ui-types";
|
||||
import { isPending } from "../../modules/pending";
|
||||
import { getFailure } from "../../modules/failure";
|
||||
import React from "react";
|
||||
|
||||
export const FETCH_REPOS = "scm/repos/FETCH_REPOS";
|
||||
export const FETCH_REPOS_PENDING = `${FETCH_REPOS}_${types.PENDING_SUFFIX}`;
|
||||
@@ -56,7 +55,9 @@ export const DELETE_REPO_PENDING = `${DELETE_REPO}_${types.PENDING_SUFFIX}`;
|
||||
export const DELETE_REPO_SUCCESS = `${DELETE_REPO}_${types.SUCCESS_SUFFIX}`;
|
||||
export const DELETE_REPO_FAILURE = `${DELETE_REPO}_${types.FAILURE_SUFFIX}`;
|
||||
|
||||
const CONTENT_TYPE = "application/vnd.scmm-repository+json;v=2";
|
||||
export const CONTENT_TYPE = "application/vnd.scmm-repository+json;v=2";
|
||||
|
||||
export const CUSTOM_NAMESPACE_STRATEGY = "CustomNamespaceStrategy";
|
||||
|
||||
// fetch repos
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import { binder, ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { File } from "@scm-manager/ui-types";
|
||||
import { DateFromNow, FileSize, Tooltip } from "@scm-manager/ui-components";
|
||||
import FileIcon from "./FileIcon";
|
||||
import { Icon } from "@scm-manager/ui-components/src";
|
||||
import { Icon } from "@scm-manager/ui-components";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import sonia.scm.util.ValidationUtil;
|
||||
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
public class RepositoryRenameDto {
|
||||
@Pattern(regexp = ValidationUtil.REGEX_REPOSITORYNAME)
|
||||
private String name;
|
||||
private String namespace;
|
||||
}
|
||||
@@ -34,11 +34,11 @@ import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.validation.Valid;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
@@ -64,8 +64,7 @@ public class RepositoryResource {
|
||||
public RepositoryResource(
|
||||
RepositoryToRepositoryDtoMapper repositoryToDtoMapper,
|
||||
RepositoryDtoToRepositoryMapper dtoToRepositoryMapper, RepositoryManager manager,
|
||||
RepositoryBasedResourceProvider resourceProvider
|
||||
) {
|
||||
RepositoryBasedResourceProvider resourceProvider) {
|
||||
this.dtoToRepositoryMapper = dtoToRepositoryMapper;
|
||||
this.manager = manager;
|
||||
this.repositoryToDtoMapper = repositoryToDtoMapper;
|
||||
@@ -79,8 +78,7 @@ public class RepositoryResource {
|
||||
* <strong>Note:</strong> This method requires "repository" privilege.
|
||||
*
|
||||
* @param namespace the namespace of the repository
|
||||
* @param name the name of the repository
|
||||
*
|
||||
* @param name the name of the repository
|
||||
*/
|
||||
@GET
|
||||
@Path("")
|
||||
@@ -118,7 +116,7 @@ public class RepositoryResource {
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
)
|
||||
)
|
||||
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name){
|
||||
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name) {
|
||||
return adapter.get(loadBy(namespace, name), repositoryToDtoMapper::map);
|
||||
}
|
||||
|
||||
@@ -128,8 +126,7 @@ public class RepositoryResource {
|
||||
* <strong>Note:</strong> This method requires "repository" privilege.
|
||||
*
|
||||
* @param namespace the namespace of the repository to delete
|
||||
* @param name the name of the repository to delete
|
||||
*
|
||||
* @param name the name of the repository to delete
|
||||
*/
|
||||
@DELETE
|
||||
@Path("")
|
||||
@@ -147,8 +144,8 @@ public class RepositoryResource {
|
||||
*
|
||||
* <strong>Note:</strong> This method requires "repository" privilege.
|
||||
*
|
||||
* @param namespace the namespace of the repository to be modified
|
||||
* @param name the name of the repository to be modified
|
||||
* @param namespace the namespace of the repository to be modified
|
||||
* @param name the name of the repository to be modified
|
||||
* @param repository repository object to modify
|
||||
*/
|
||||
@PUT
|
||||
@@ -176,6 +173,37 @@ public class RepositoryResource {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the given repository.
|
||||
*
|
||||
* <strong>Note:</strong> This method requires "repository" privilege.
|
||||
*
|
||||
* @param namespace the namespace of the repository to be modified
|
||||
* @param name the name of the repository to be modified
|
||||
* @param renameDto renameDto object to modify
|
||||
*/
|
||||
@POST
|
||||
@Path("rename")
|
||||
@Consumes(VndMediaType.REPOSITORY)
|
||||
@Operation(summary = "Rename repository", description = "Renames the repository for the given namespace and name.", tags = "Repository")
|
||||
@ApiResponse(responseCode = "204", description = "update success")
|
||||
@ApiResponse(responseCode = "400", description = "invalid body, e.g. illegal change of namespace or name")
|
||||
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"repository:renameDto\" privilege")
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "not found, no repository with the specified namespace and name available",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
))
|
||||
@ApiResponse(responseCode = "500", description = "internal server error")
|
||||
public Response rename(@PathParam("namespace") String namespace, @PathParam("name") String name, @Valid RepositoryRenameDto renameDto) {
|
||||
Repository repository = loadBy(namespace, name).get();
|
||||
manager.rename(repository, renameDto.getNamespace(), renameDto.getName());
|
||||
return Response.status(204).build();
|
||||
}
|
||||
|
||||
private Repository processUpdate(RepositoryDto repositoryDto, Repository existing) {
|
||||
Repository changedRepository = dtoToRepositoryMapper.map(repositoryDto, existing.getId());
|
||||
changedRepository.setPermissions(existing.getPermissions());
|
||||
|
||||
@@ -24,14 +24,15 @@
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import de.otto.edison.hal.Embedded;
|
||||
import de.otto.edison.hal.Link;
|
||||
import de.otto.edison.hal.Links;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ObjectFactory;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.repository.Feature;
|
||||
import sonia.scm.repository.HealthCheckFailure;
|
||||
import sonia.scm.repository.NamespaceStrategy;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryPermissions;
|
||||
import sonia.scm.repository.api.Command;
|
||||
@@ -41,7 +42,9 @@ import sonia.scm.repository.api.ScmProtocol;
|
||||
import sonia.scm.web.EdisonHalAppender;
|
||||
import sonia.scm.web.api.RepositoryToHalMapper;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static de.otto.edison.hal.Embedded.embeddedBuilder;
|
||||
import static de.otto.edison.hal.Link.link;
|
||||
@@ -56,7 +59,11 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
|
||||
@Inject
|
||||
private ResourceLinks resourceLinks;
|
||||
@Inject
|
||||
private ScmConfiguration scmConfiguration;
|
||||
@Inject
|
||||
private RepositoryServiceFactory serviceFactory;
|
||||
@Inject
|
||||
private Set<NamespaceStrategy> strategies;
|
||||
|
||||
abstract HealthCheckFailureDto toDto(HealthCheckFailure failure);
|
||||
|
||||
@@ -72,6 +79,13 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
|
||||
if (RepositoryPermissions.modify(repository).isPermitted()) {
|
||||
linksBuilder.single(link("update", resourceLinks.repository().update(repository.getNamespace(), repository.getName())));
|
||||
}
|
||||
if (RepositoryPermissions.rename(repository).isPermitted()) {
|
||||
if (isRenameNamespacePossible()) {
|
||||
linksBuilder.single(link("renameWithNamespace", resourceLinks.repository().rename(repository.getNamespace(), repository.getName())));
|
||||
} else {
|
||||
linksBuilder.single(link("rename", resourceLinks.repository().rename(repository.getNamespace(), repository.getName())));
|
||||
}
|
||||
}
|
||||
if (RepositoryPermissions.permissionRead(repository).isPermitted()) {
|
||||
linksBuilder.single(link("permissions", resourceLinks.repositoryPermission().all(repository.getNamespace(), repository.getName())));
|
||||
}
|
||||
@@ -105,6 +119,15 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
|
||||
return new RepositoryDto(linksBuilder.build(), embeddedBuilder.build());
|
||||
}
|
||||
|
||||
private boolean isRenameNamespacePossible() {
|
||||
for (NamespaceStrategy strategy : strategies) {
|
||||
if (strategy.getClass().getSimpleName().equals(scmConfiguration.getNamespaceStrategy())) {
|
||||
return strategy.canBeChanged();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Link createProtocolLink(ScmProtocol protocol) {
|
||||
return Link.linkBuilder("protocol", protocol.getUrl()).withName(protocol.getType()).build();
|
||||
}
|
||||
|
||||
@@ -279,6 +279,10 @@ class ResourceLinks {
|
||||
String update(String namespace, String name) {
|
||||
return repositoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("update").parameters().href();
|
||||
}
|
||||
|
||||
String rename(String namespace, String name) {
|
||||
return repositoryLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("rename").parameters().href();
|
||||
}
|
||||
}
|
||||
|
||||
RepositoryCollectionLinks repositoryCollection() {
|
||||
|
||||
@@ -41,4 +41,9 @@ public class CustomNamespaceStrategy implements NamespaceStrategy {
|
||||
|
||||
return namespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeChanged() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,11 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
import com.github.sdorra.ssp.PermissionActionCheck;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.google.inject.Inject;
|
||||
@@ -35,6 +36,7 @@ import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.ConfigurationException;
|
||||
import sonia.scm.HandlerEventType;
|
||||
import sonia.scm.ManagerDaoAdapter;
|
||||
import sonia.scm.NoChangesMadeException;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.Type;
|
||||
@@ -83,7 +85,6 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
|
||||
private final Provider<NamespaceStrategy> namespaceStrategyProvider;
|
||||
private final ManagerDaoAdapter<Repository> managerDaoAdapter;
|
||||
|
||||
|
||||
@Inject
|
||||
public DefaultRepositoryManager(ScmConfiguration configuration,
|
||||
SCMContextProvider contextProvider, KeyGenerator keyGenerator,
|
||||
@@ -154,7 +155,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Repository repository){
|
||||
public void delete(Repository repository) {
|
||||
logger.info("delete repository {}/{} of type {}", repository.getNamespace(), repository.getName(), repository.getType());
|
||||
managerDaoAdapter.delete(
|
||||
repository,
|
||||
@@ -179,7 +180,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modify(Repository repository){
|
||||
public void modify(Repository repository) {
|
||||
logger.info("modify repository {}/{} of type {}", repository.getNamespace(), repository.getName(), repository.getType());
|
||||
|
||||
managerDaoAdapter.modify(
|
||||
@@ -243,6 +244,40 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public Repository rename(Repository repository, String newNamespace, String newName) {
|
||||
|
||||
if (hasNamespaceOrNameNotChanged(repository, newNamespace, newName)) {
|
||||
throw new NoChangesMadeException(repository);
|
||||
}
|
||||
|
||||
Repository changedRepository = repository.clone();
|
||||
if (!Strings.isNullOrEmpty(newName)) {
|
||||
changedRepository.setName(newName);
|
||||
}
|
||||
|
||||
if (!Strings.isNullOrEmpty(newNamespace) && !repository.getNamespace().equals(newNamespace)) {
|
||||
NamespaceStrategy strategy = namespaceStrategyProvider.get();
|
||||
if (!strategy.canBeChanged()) {
|
||||
throw new ChangeNamespaceNotAllowedException(repository);
|
||||
}
|
||||
changedRepository.setNamespace(strategy.createNamespace(changedRepository));
|
||||
}
|
||||
|
||||
managerDaoAdapter.modify(
|
||||
changedRepository,
|
||||
RepositoryPermissions::rename,
|
||||
notModified -> {
|
||||
},
|
||||
notModified -> fireEvent(HandlerEventType.MODIFY, changedRepository, repository));
|
||||
|
||||
return changedRepository;
|
||||
}
|
||||
|
||||
private boolean hasNamespaceOrNameNotChanged(Repository repository, String newNamespace, String newName) {
|
||||
return repository.getName().equals(newName)
|
||||
&& repository.getNamespace().equals(newNamespace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Repository> getAll(Predicate<Repository> filter, Comparator<Repository> comparator) {
|
||||
List<Repository> repositories = Lists.newArrayList();
|
||||
@@ -345,8 +380,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager {
|
||||
types.add(type);
|
||||
}
|
||||
|
||||
private RepositoryHandler getHandler(Repository repository)
|
||||
{
|
||||
private RepositoryHandler getHandler(Repository repository) {
|
||||
String type = repository.getType();
|
||||
RepositoryHandler handler = handlerMap.get(type);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.update.security;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -44,6 +44,7 @@ import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@@ -179,7 +180,7 @@ public class XmlSecurityV1UpdateStep implements UpdateStep {
|
||||
@XmlRootElement(name = "configuration")
|
||||
private static class V1Security {
|
||||
@XmlElement(name = "entry")
|
||||
private List<Entry> entries;
|
||||
private List<Entry> entries = new ArrayList<>();
|
||||
}
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
|
||||
@@ -35,6 +35,9 @@
|
||||
<permission>
|
||||
<value>repository:read,pull,push:*</value>
|
||||
</permission>
|
||||
<permission>
|
||||
<value>repository:read,rename:*</value>
|
||||
</permission>
|
||||
<permission>
|
||||
<value>repository:*</value>
|
||||
</permission>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<verb>read</verb>
|
||||
<verb>modify</verb>
|
||||
<verb>delete</verb>
|
||||
<verb>rename</verb>
|
||||
<verb>pull</verb>
|
||||
<verb>push</verb>
|
||||
<verb>permissionRead</verb>
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
"description": "Darf alle Repositories lesen, klonen und schreiben."
|
||||
}
|
||||
},
|
||||
"read,rename": {
|
||||
"*": {
|
||||
"displayName": "Alle Repositories umbenennen",
|
||||
"description": "Darf alle Repositories lesen und umbenennen."
|
||||
}
|
||||
},
|
||||
"*": {
|
||||
"displayName": "Alle Repositories besitzen (Owner)",
|
||||
"description": "Darf alle Repositories lesen, klonen, schreiben, konfigurieren und löschen."
|
||||
@@ -103,6 +109,10 @@
|
||||
"displayName": "Repository Löschen",
|
||||
"description": "Darf das Repository löschen."
|
||||
},
|
||||
"rename": {
|
||||
"displayName": "Repository umbenennen",
|
||||
"description": "Darf das Repository umbenennen."
|
||||
},
|
||||
"pull": {
|
||||
"displayName": "Pull/Checkout",
|
||||
"description": "Darf pull/checkout auf das Repository ausführen."
|
||||
@@ -187,6 +197,10 @@
|
||||
"displayName": "Es wurden keine Änderungen durchgeführt",
|
||||
"description": "Das Repository wurde nicht verändert. Daher konnte kein neuer Commit erzeugt werden. Womöglich werden Änderungen aufgrund einer .ignore-Datei Definition nicht berücksichtigt."
|
||||
},
|
||||
"ERS2vYb7U1": {
|
||||
"displayName": "Änderung des Namespace nicht möglich",
|
||||
"description": "Namespaces dürfen nur mit der Namespace Strategie \"Benutzerdefiniert\" verändert werden."
|
||||
},
|
||||
"4iRct4avG1": {
|
||||
"displayName": "Die Revisionen haben keinen gemeinsamen Ursprung",
|
||||
"description": "Die Historie der Revisionen hat keinen gemeinsamen Urspung und kann somit auch nicht gegen einen solchen verglichen werden."
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
"description": "May see, clone and push to all repositories"
|
||||
}
|
||||
},
|
||||
"read,rename": {
|
||||
"*": {
|
||||
"displayName": "Rename all repositories",
|
||||
"description": "May see and rename all repositories"
|
||||
}
|
||||
},
|
||||
"*": {
|
||||
"displayName": "Own all repositories",
|
||||
"description": "May see, clone, push to, configure and delete all repositories"
|
||||
@@ -103,6 +109,10 @@
|
||||
"displayName": "delete repository",
|
||||
"description": "May delete the repository"
|
||||
},
|
||||
"rename": {
|
||||
"displayName": "rename repository",
|
||||
"description": "May rename the repository."
|
||||
},
|
||||
"pull": {
|
||||
"displayName": "pull/checkout repository",
|
||||
"description": "May pull/checkout the repository"
|
||||
@@ -187,6 +197,10 @@
|
||||
"displayName": "No changes were made",
|
||||
"description": "No changes were made to the files of the repository. Therefor no new commit could be created. Possibly changes cannot be applied due to an .ignore-File definition."
|
||||
},
|
||||
"ERS2vYb7U1": {
|
||||
"displayName": "Illegal change of namespace",
|
||||
"description": "Namespaces can only be changed if namespace strategy is \"custom\"."
|
||||
},
|
||||
"4iRct4avG1": {
|
||||
"displayName": "The revisions have unrelated histories",
|
||||
"description": "The revisions have unrelated histories. Therefor there is no common commit to compare with."
|
||||
|
||||
@@ -26,8 +26,8 @@ package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.github.sdorra.shiro.ShiroRule;
|
||||
import com.github.sdorra.shiro.SubjectAware;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.io.Resources;
|
||||
import com.google.inject.util.Providers;
|
||||
import org.apache.shiro.subject.SimplePrincipalCollection;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||
@@ -40,7 +40,10 @@ import org.mockito.Captor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import sonia.scm.PageResult;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.repository.CustomNamespaceStrategy;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.NamespaceStrategy;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryInitializer;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
@@ -56,6 +59,7 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
@@ -72,6 +76,7 @@ import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyObject;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -103,6 +108,10 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
|
||||
private ScmPathInfo uriInfo;
|
||||
@Mock
|
||||
private RepositoryInitializer repositoryInitializer;
|
||||
@Mock
|
||||
private ScmConfiguration configuration;
|
||||
@Mock
|
||||
private Set<NamespaceStrategy> strategies;
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<Predicate<Repository>> filterCaptor;
|
||||
@@ -127,6 +136,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
|
||||
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
|
||||
when(scmPathInfoStore.get()).thenReturn(uriInfo);
|
||||
when(uriInfo.getApiRestUri()).thenReturn(URI.create("/x/y"));
|
||||
doReturn(ImmutableSet.of(new CustomNamespaceStrategy()).iterator()).when(strategies).iterator();
|
||||
SimplePrincipalCollection trillian = new SimplePrincipalCollection("trillian", REALM);
|
||||
trillian.add(new User("trillian"), REALM);
|
||||
shiro.setSubject(
|
||||
@@ -152,6 +162,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
|
||||
@Test
|
||||
public void shouldFindExistingRepository() throws URISyntaxException, UnsupportedEncodingException {
|
||||
mockRepository("space", "repo");
|
||||
when(configuration.getNamespaceStrategy()).thenReturn("CustomNamespaceStrategy");
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo");
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
@@ -166,6 +177,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
|
||||
public void shouldGetAll() throws URISyntaxException, UnsupportedEncodingException {
|
||||
PageResult<Repository> singletonPageResult = createSingletonPageResult(mockRepository("space", "repo"));
|
||||
when(repositoryManager.getPage(any(), any(), eq(0), eq(10))).thenReturn(singletonPageResult);
|
||||
when(configuration.getNamespaceStrategy()).thenReturn("CustomNamespaceStrategy");
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
@@ -180,6 +192,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
|
||||
public void shouldCreateFilterForSearch() throws URISyntaxException {
|
||||
PageResult<Repository> singletonPageResult = createSingletonPageResult(mockRepository("space", "repo"));
|
||||
when(repositoryManager.getPage(filterCaptor.capture(), any(), eq(0), eq(10))).thenReturn(singletonPageResult);
|
||||
when(configuration.getNamespaceStrategy()).thenReturn("CustomNamespaceStrategy");
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "?q=Rep");
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
@@ -362,6 +375,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
|
||||
public void shouldCreateArrayOfProtocolUrls() throws Exception {
|
||||
mockRepository("space", "repo");
|
||||
when(service.getSupportedProtocols()).thenReturn(of(new MockScmProtocol("http", "http://"), new MockScmProtocol("ssh", "ssh://")));
|
||||
when(configuration.getNamespaceStrategy()).thenReturn("CustomNamespaceStrategy");
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo");
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
@@ -372,6 +386,28 @@ public class RepositoryRootResourceTest extends RepositoryTestBase {
|
||||
assertTrue(response.getContentAsString().contains("\"protocol\":[{\"href\":\"http://\",\"name\":\"http\"},{\"href\":\"ssh://\",\"name\":\"ssh\"}]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRenameRepository() throws Exception {
|
||||
String namespace = "space";
|
||||
String name = "repo";
|
||||
Repository repository1 = mockRepository(namespace, name);
|
||||
when(manager.get(new NamespaceAndName(namespace, name))).thenReturn(repository1);
|
||||
|
||||
URL url = Resources.getResource("sonia/scm/api/v2/rename-repo.json");
|
||||
byte[] repository = Resources.toByteArray(url);
|
||||
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/rename")
|
||||
.contentType(VndMediaType.REPOSITORY)
|
||||
.content(repository);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(SC_NO_CONTENT, response.getStatus());
|
||||
verify(repositoryManager).rename(repository1, "space", "x");
|
||||
}
|
||||
|
||||
private PageResult<Repository> createSingletonPageResult(Repository repository) {
|
||||
return new PageResult<>(singletonList(repository), 0);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ package sonia.scm.api.v2.resources;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
|
||||
import static com.google.inject.util.Providers.of;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
abstract class RepositoryTestBase {
|
||||
|
||||
@@ -47,7 +46,6 @@ abstract class RepositoryTestBase {
|
||||
RepositoryCollectionResource repositoryCollectionResource;
|
||||
AnnotateResource annotateResource;
|
||||
|
||||
|
||||
RepositoryRootResource getRepositoryRootResource() {
|
||||
RepositoryBasedResourceProvider repositoryBasedResourceProvider = new RepositoryBasedResourceProvider(
|
||||
of(tagRootResource),
|
||||
@@ -66,8 +64,7 @@ abstract class RepositoryTestBase {
|
||||
repositoryToDtoMapper,
|
||||
dtoToRepositoryMapper,
|
||||
manager,
|
||||
repositoryBasedResourceProvider
|
||||
)),
|
||||
repositoryBasedResourceProvider)),
|
||||
of(repositoryCollectionResource));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,12 @@
|
||||
* 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 com.google.common.collect.ImmutableSet;
|
||||
import org.apache.shiro.util.ThreadContext;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -33,7 +34,10 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.repository.CustomNamespaceStrategy;
|
||||
import sonia.scm.repository.HealthCheckFailure;
|
||||
import sonia.scm.repository.NamespaceStrategy;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.api.Command;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
@@ -41,13 +45,16 @@ import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||
import sonia.scm.repository.api.ScmProtocol;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Collections.singleton;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.stream.Stream.of;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
@@ -72,6 +79,10 @@ public class RepositoryToRepositoryDtoMapperTest {
|
||||
private ScmPathInfoStore scmPathInfoStore;
|
||||
@Mock
|
||||
private ScmPathInfo uriInfo;
|
||||
@Mock
|
||||
private ScmConfiguration configuration;
|
||||
@Mock
|
||||
private Set<NamespaceStrategy> strategies;
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryToRepositoryDtoMapperImpl mapper;
|
||||
@@ -83,7 +94,9 @@ public class RepositoryToRepositoryDtoMapperTest {
|
||||
when(repositoryService.isSupported(any(Command.class))).thenReturn(true);
|
||||
when(repositoryService.getSupportedProtocols()).thenReturn(of());
|
||||
when(scmPathInfoStore.get()).thenReturn(uriInfo);
|
||||
when(configuration.getNamespaceStrategy()).thenReturn("CustomNamespaceStrategy");
|
||||
when(uriInfo.getApiRestUri()).thenReturn(URI.create("/x/y"));
|
||||
doReturn(ImmutableSet.of(new CustomNamespaceStrategy()).iterator()).when(strategies).iterator();
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -129,6 +142,23 @@ public class RepositoryToRepositoryDtoMapperTest {
|
||||
dto.getLinks().getLinkBy("update").get().getHref());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateRenameLink() {
|
||||
when(configuration.getNamespaceStrategy()).thenReturn("test");
|
||||
RepositoryDto dto = mapper.map(createTestRepository());
|
||||
assertEquals(
|
||||
"http://example.com/base/v2/repositories/testspace/test/rename",
|
||||
dto.getLinks().getLinkBy("rename").get().getHref());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateRenameWithNamespaceLink() {
|
||||
RepositoryDto dto = mapper.map(createTestRepository());
|
||||
assertEquals(
|
||||
"http://example.com/base/v2/repositories/testspace/test/rename",
|
||||
dto.getLinks().getLinkBy("renameWithNamespace").get().getHref());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapHealthCheck() {
|
||||
RepositoryDto dto = mapper.map(createTestRepository());
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -43,6 +43,7 @@ import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -107,15 +108,17 @@ class XmlSecurityV1UpdateStepTest {
|
||||
@Nested
|
||||
class WithExistingSecurityXml {
|
||||
|
||||
private Path configDir;
|
||||
|
||||
@BeforeEach
|
||||
void createSecurityV1XML(@TempDir Path tempDir) throws IOException {
|
||||
Path configDir = tempDir.resolve("config");
|
||||
configDir = tempDir.resolve("config");
|
||||
Files.createDirectories(configDir);
|
||||
copyTestDatabaseFile(configDir, "securityV1.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMapV1PermissionsFromSecurityV1XML() throws JAXBException {
|
||||
void shouldMapV1PermissionsFromSecurityV1XML() throws IOException, JAXBException {
|
||||
copyTestDatabaseFile(configDir, "securityV1.xml");
|
||||
updateStep.doUpdate();
|
||||
List<String> assignedPermission =
|
||||
assignedPermissionStore.getAll().values()
|
||||
@@ -127,15 +130,27 @@ class XmlSecurityV1UpdateStepTest {
|
||||
assertThat(assignedPermission).contains("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotFailOnEmptyV1SecurityXml() throws IOException, JAXBException {
|
||||
copyTestDatabaseFile(configDir, "emptySecurityV1.xml", "securityV1.xml");
|
||||
updateStep.doUpdate();
|
||||
assertThat(assignedPermissionStore.getAll()).isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void copyTestDatabaseFile(Path configDir, String fileName) throws IOException {
|
||||
URL url = Resources.getResource("sonia/scm/update/security/" + fileName);
|
||||
Files.copy(url.openStream(), configDir.resolve(fileName));
|
||||
copyTestDatabaseFile(configDir, fileName, fileName);
|
||||
}
|
||||
|
||||
private void copyTestDatabaseFile(Path configDir, String sourceFileName, String targetFileName) throws IOException {
|
||||
URL url = Resources.getResource("sonia/scm/update/security/" + sourceFileName);
|
||||
Files.copy(url.openStream(), configDir.resolve(targetFileName));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotFailForMissingConfigDir() throws JAXBException {
|
||||
updateStep.doUpdate();
|
||||
assertThat(assignedPermissionStore.getAll()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "x",
|
||||
"namespace": "space"
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.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.
|
||||
|
||||
-->
|
||||
<configuration>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user