Skip to content

Commit

Permalink
fix: Add Bitbucket type checking for scm/ stripping (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
pstreef authored Jul 15, 2024
1 parent 79df7c0 commit 72709e0
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 39 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,23 @@ file is included in this repository.

### Mapping repositories

Use [scm-origins.txt](src/main/resources/scm-origins.txt) to list all the origins (host + context path) for the SCM providers listed in the repos.csv.
Edit [application.yaml](src/main/resources/application.yaml) to list all the origins (host + context path) for the SCM providers listed in the repos.csv, and provide a type: `[GITHUB,GITLAB,BITBUCKET_CLOUD,BITBUCKET]`.
This will be used to split the clone url into an origin and path.

Note that for an on-premise Bitbucket (server) we should not have the `scm/` path segment in the origin or the path. This will automatically be stripped off if you configure this as explained above.

Example:
If you have a repository at `cloneUrl=https://bitbucket.example.com/stash/scm/openrewrite/rewrite.git` and supply `bitbucket.example.com/stash/scm` it will create a repository:
If you have a repository at `cloneUrl=https://bitbucket.example.com/stash/scm/openrewrite/rewrite.git` and supply `bitbucket.example.com/stash` it will create a repository:

```
{
"origin": "bitbucket.example.com/stash/scm",
"origin": "bitbucket.example.com/stash",
"path": "openrewrite/rewrite",
"branch": "main"
}
```

you can set `organizations.allow-missing-scm-origins` in [application.yaml](src/main/resources/application.yaml) to true if you want to strictly check on startup that all origins in repos.csv are present.
you can set `moderne.scm.allow-missing-scm-origins` in [application.yaml](src/main/resources/application.yaml) to true if you want to strictly check on startup that all origins in repos.csv are present.

### Commit options
The `commitOptions` field on the `Organization` type is a list of strings that represent the commit options that are
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/io/moderne/organizations/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@EnableConfigurationProperties(ScmConfiguration.class)
@SpringBootApplication
public class Application {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import org.openrewrite.internal.lang.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

Expand All @@ -25,14 +24,13 @@
public class OrganizationStructureService {
private static final String REPOS_CSV = "repos.csv";
private static final String NAME_MAPPING = "id-mapping.txt";
private static final String SCM_ORIGINS = "scm-origins.txt";
private static final Logger log = LoggerFactory.getLogger(OrganizationStructureService.class.getName());
private final RepositoryMapper repositoryMapper = new RepositoryMapper();
private final ScmConfiguration scmConfiguration;
private final RepositoryMapper repositoryMapper;

private final boolean allowMissingScmOrigins;

public OrganizationStructureService(@Value("${organizations.allow-missing-scm-origins:true}") boolean allowMissingScmOrigins) {
this.allowMissingScmOrigins = allowMissingScmOrigins;
public OrganizationStructureService(ScmConfiguration scmConfiguration) {
this.scmConfiguration = scmConfiguration;
repositoryMapper = new RepositoryMapper(scmConfiguration);
}

public Map<String, OrganizationRepositories> readOrganizationStructure() {
Expand All @@ -53,11 +51,11 @@ public Map<String, OrganizationRepositories> readOrganizationStructure() {
String branch = fields[1].trim();
RepositoryInput repository = repositoryMapper.determineRepository(cloneUrl, branch);
if (repository == null) {
if (allowMissingScmOrigins) {
if (scmConfiguration.isAllowMissingScmOrigins()) {
log.warn("No scm origin found for %s. Consider adding it to scm-origins.txt".formatted(cloneUrl));
return;
} else {
throw new IllegalStateException("No scm origin found for %s. Add it to scm-origins.txt or set organizations.allow-missing-scm-origins to true".formatted(cloneUrl));
throw new IllegalStateException("No scm origin found for %s. Add it to moderne.scm.repositories or set moderne.scm.allow-missing-scm-origins to true".formatted(cloneUrl));
}
}
allRepositories.add(repository);
Expand Down Expand Up @@ -140,31 +138,22 @@ public static void printTree(Map<String, List<OrganizationRepositories>> tree, S
}

private static class RepositoryMapper {
Map<String, Pattern> urlPatterns = new LinkedHashMap<>();

private RepositoryMapper() {
List<String> scmOrigins = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new ClassPathResource(SCM_ORIGINS).getInputStream()))) {
reader.lines()
.filter(s -> !s.startsWith("#"))
.forEach(scmOrigins::add);
} catch (IOException e) {
throw new RuntimeException(e);
}
Map<ScmConfiguration.ScmRepository, Pattern> urlPatterns = new LinkedHashMap<>();

for (String origin : scmOrigins) {
Pattern pattern = Pattern.compile(origin + "(.*)");
urlPatterns.put(origin, pattern);
private RepositoryMapper(ScmConfiguration scmConfiguration) {
for (ScmConfiguration.ScmRepository repository : scmConfiguration.getRepositories()) {
Pattern pattern = Pattern.compile(repository.getOrigin() + "(.*)");
urlPatterns.put(repository, pattern);
}
}

@Nullable
public RepositoryInput determineRepository(String cloneUrl, String branch) {
for (Map.Entry<String, Pattern> entry : urlPatterns.entrySet()) {
String origin = cleanOrigin(entry.getKey());
for (Map.Entry<ScmConfiguration.ScmRepository, Pattern> entry : urlPatterns.entrySet()) {
String origin = cleanOrigin(entry.getKey().getOrigin());
Matcher matcher = entry.getValue().matcher(cloneUrl);
if (matcher.find()) {
String path = cleanPath(matcher.group(1));
String path = cleanPath(matcher.group(1), entry.getKey().getType());
return new RepositoryInput(path, origin, branch);
}
}
Expand All @@ -186,14 +175,14 @@ private static String cleanOrigin(String origin) {
return origin;
}

private static String cleanPath(String path) {
private static String cleanPath(String path, ScmConfiguration.ScmRepository.Type type) {
if (path.startsWith("/")) {
path = path.substring(1);
}
// In case of bitbucket server/on prem we need to remove the `/scm` prefix.
// This prefix is not part of all URL's to repository resource
// (for instance pull requests) so it cannot be part of the origin or path.
if (path.startsWith("scm/")) {
if (type == ScmConfiguration.ScmRepository.Type.BITBUCKET && path.startsWith("scm/")) {
path = path.substring(4);
}
if (path.endsWith(".git")) {
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/io/moderne/organizations/ScmConfiguration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.moderne.organizations;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;

@ConfigurationProperties(prefix = "moderne.scm")
public class ScmConfiguration {
private List<ScmRepository> repositories;
private boolean allowMissingScmOrigins;

public static class ScmRepository {
String origin;
Type type;

public String getOrigin() {
return origin;
}

public void setOrigin(String origin) {
this.origin = origin;
}

public Type getType() {
return type;
}

public void setType(Type type) {
this.type = type;
}

public enum Type {
GITHUB, BITBUCKET_CLOUD, GITLAB, BITBUCKET
}
}

public List<ScmRepository> getRepositories() {
return repositories;
}

public void setRepositories(List<ScmRepository> repositories) {
this.repositories = repositories;
}

public boolean isAllowMissingScmOrigins() {
return allowMissingScmOrigins;
}

public void setAllowMissingScmOrigins(boolean allowMissingScmOrigins) {
this.allowMissingScmOrigins = allowMissingScmOrigins;
}
}
14 changes: 13 additions & 1 deletion src/main/resources/application.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
management.endpoints.web.exposure.include: prometheus,health
management.endpoint.prometheus.enabled: true
management.endpoint.health.enabled: true
management.endpoint.health.enabled: true
moderne:
scm:
allowMissingScmOrigins: true
repositories:
- origin: github.com
type: GITHUB
- origin: bitbucket.org
type: BITBUCKET_CLOUD
- origin: gitlab.com
type: GITLAB
- origin: https://bitbucket.example.com/stash
type: BITBUCKET
5 changes: 0 additions & 5 deletions src/main/resources/scm-origins.txt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@

import io.moderne.organizations.types.RepositoryInput;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability;
import org.springframework.boot.test.context.SpringBootTest;

import static org.assertj.core.api.Assertions.assertThat;

@AutoConfigureObservability
@SpringBootTest(properties = {
"moderne.scm.allow-missing-scm-origins=false"
})
class OrganizationStructureServiceTest {

@Autowired
OrganizationStructureService structureService;

@Test
void canReadAllOrganizations() {
new OrganizationStructureService(false).readOrganizationStructure();
structureService.readOrganizationStructure();
}

@Test
Expand Down

0 comments on commit 72709e0

Please sign in to comment.