create RepositoryInitializer which can be used to create new files in the initial commit on a new repository

This commit is contained in:
Eduard Heimbuch
2020-01-20 15:19:51 +01:00
parent af866f2ae8
commit f44d17d640
10 changed files with 494 additions and 13 deletions

View File

@@ -7,6 +7,7 @@ import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import org.apache.shiro.SecurityUtils;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryInitializer;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.repository.RepositoryPermission;
import sonia.scm.search.SearchRequest;
@@ -24,7 +25,7 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import static com.google.common.base.Strings.isNullOrEmpty;
@@ -38,13 +39,15 @@ public class RepositoryCollectionResource {
private final RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper;
private final RepositoryDtoToRepositoryMapper dtoToRepositoryMapper;
private final ResourceLinks resourceLinks;
private final RepositoryInitializer repositoryInitializer;
@Inject
public RepositoryCollectionResource(RepositoryManager manager, RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper, RepositoryDtoToRepositoryMapper dtoToRepositoryMapper, ResourceLinks resourceLinks) {
public RepositoryCollectionResource(RepositoryManager manager, RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper, RepositoryDtoToRepositoryMapper dtoToRepositoryMapper, ResourceLinks resourceLinks, RepositoryInitializer repositoryInitializer) {
this.adapter = new CollectionResourceManagerAdapter<>(manager, Repository.class);
this.repositoryCollectionToDtoMapper = repositoryCollectionToDtoMapper;
this.dtoToRepositoryMapper = dtoToRepositoryMapper;
this.resourceLinks = resourceLinks;
this.repositoryInitializer = repositoryInitializer;
}
/**
@@ -68,10 +71,10 @@ public class RepositoryCollectionResource {
@ResponseCode(code = 500, condition = "internal server error")
})
public Response getAll(@DefaultValue("0") @QueryParam("page") int page,
@DefaultValue("" + DEFAULT_PAGE_SIZE) @QueryParam("pageSize") int pageSize,
@QueryParam("sortBy") String sortBy,
@DefaultValue("false") @QueryParam("desc") boolean desc,
@DefaultValue("") @QueryParam("q") String search
@DefaultValue("" + DEFAULT_PAGE_SIZE) @QueryParam("pageSize") int pageSize,
@QueryParam("sortBy") String sortBy,
@DefaultValue("false") @QueryParam("desc") boolean desc,
@DefaultValue("") @QueryParam("q") String search
) {
return adapter.getAll(page, pageSize, createSearchPredicate(search), sortBy, desc,
pageResult -> repositoryCollectionToDtoMapper.map(page, pageSize, pageResult));
@@ -81,7 +84,7 @@ public class RepositoryCollectionResource {
* Creates a new repository.
*
* <strong>Note:</strong> This method requires "repository" privilege. The namespace of the given repository will
* be ignored and set by the configured namespace strategy.
* be ignored and set by the configured namespace strategy.
*
* @param repository The repository to be created.
* @return A response with the link to the new repository (if created successfully).
@@ -98,10 +101,18 @@ public class RepositoryCollectionResource {
})
@TypeHint(TypeHint.NO_CONTENT.class)
@ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created repository"))
public Response create(@Valid RepositoryDto repository) {
return adapter.create(repository,
public Response create(@Valid RepositoryDto repository, @QueryParam("initialize") boolean initialize) {
AtomicReference<Repository> reference = new AtomicReference<>();
Response response = adapter.create(repository,
() -> createModelObjectFromDto(repository),
r -> resourceLinks.repository().self(r.getNamespace(), r.getName()));
r -> {
reference.set(r);
return resourceLinks.repository().self(r.getNamespace(), r.getName());
});
if (initialize) {
repositoryInitializer.initialize(reference.get());
}
return response;
}
private Repository createModelObjectFromDto(@Valid RepositoryDto repositoryDto) {

View File

@@ -0,0 +1,23 @@
package sonia.scm.repository;
import com.google.common.base.Strings;
import sonia.scm.Priority;
import sonia.scm.plugin.Extension;
import java.io.IOException;
@Extension
@Priority(1) // should always be the first, so that plugins can overwrite the readme.md
public class ReadmeRepositoryContentInitializer implements RepositoryContentInitializer {
@Override
public void initialize(InitializerContext context) throws IOException {
Repository repository = context.getRepository();
String content = "# " + repository.getName();
String description = repository.getDescription();
if (!Strings.isNullOrEmpty(description)) {
content += "\n\n" + description;
}
context.create("README.md").from(content);
}
}

View File

@@ -0,0 +1,101 @@
package sonia.scm.repository;
import com.google.common.io.ByteSource;
import com.google.common.io.CharSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.Priorities;
import sonia.scm.repository.api.ModifyCommandBuilder;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Set;
@Singleton
public class RepositoryInitializer {
private static final Logger LOG = LoggerFactory.getLogger(RepositoryInitializer.class);
private final RepositoryServiceFactory serviceFactory;
private final Iterable<RepositoryContentInitializer> contentInitializers;
@Inject
public RepositoryInitializer(RepositoryServiceFactory serviceFactory, Set<RepositoryContentInitializer> contentInitializerSet) {
this.serviceFactory = serviceFactory;
this.contentInitializers = Priorities.sortInstances(contentInitializerSet);
}
public void initialize(Repository repository) {
try (RepositoryService service = serviceFactory.create(repository)) {
ModifyCommandBuilder modifyCommandBuilder = service.getModifyCommand();
InitializerContextImpl initializerContext = new InitializerContextImpl(repository, modifyCommandBuilder);
for (RepositoryContentInitializer initializer : contentInitializers) {
initializer.initialize(initializerContext);
}
modifyCommandBuilder.setCommitMessage("initialize repository");
String revision = modifyCommandBuilder.execute();
LOG.info("initialized repository {} as revision {}", repository.getNamespaceAndName(), revision);
} catch (IOException e) {
throw new InternalRepositoryException(repository, "failed to initialize repository", e);
}
}
private class InitializerContextImpl implements RepositoryContentInitializer.InitializerContext {
private final Repository repository;
private final ModifyCommandBuilder builder;
InitializerContextImpl(Repository repository, ModifyCommandBuilder builder) {
this.repository = repository;
this.builder = builder;
}
@Override
public Repository getRepository() {
return repository;
}
@Override
public RepositoryContentInitializer.CreateFile create(String path) {
return new CreateFileImpl(this, builder.createFile(path).setOverwrite(true));
}
}
private class CreateFileImpl implements RepositoryContentInitializer.CreateFile {
private final RepositoryContentInitializer.InitializerContext initializerContext;
private final ModifyCommandBuilder.WithOverwriteFlagContentLoader contentLoader;
CreateFileImpl(RepositoryContentInitializer.InitializerContext initializerContext, ModifyCommandBuilder.WithOverwriteFlagContentLoader contentLoader) {
this.initializerContext = initializerContext;
this.contentLoader = contentLoader;
}
@Override
public RepositoryContentInitializer.InitializerContext from(String content) throws IOException {
return from(CharSource.wrap(content).asByteSource(StandardCharsets.UTF_8));
}
@Override
public RepositoryContentInitializer.InitializerContext from(InputStream input) throws IOException {
contentLoader.withData(input);
return initializerContext;
}
@Override
public RepositoryContentInitializer.InitializerContext from(ByteSource byteSource) throws IOException {
contentLoader.withData(byteSource);
return initializerContext;
}
}
}