checkState(!this.branch.isInitialized(), "Branch has already been set"); | checkState(!this.branch.isInitialized(), "Branch has already been set"); | ||||
boolean isCommunityEdition = editionProvider.get().filter(t -> t == EditionProvider.Edition.COMMUNITY).isPresent(); | boolean isCommunityEdition = editionProvider.get().filter(t -> t == EditionProvider.Edition.COMMUNITY).isPresent(); | ||||
checkState( | checkState( | ||||
!isCommunityEdition || branch.isMain() || branch.isLegacyFeature(), | |||||
!isCommunityEdition || branch.isMain(), | |||||
"Branches and Pull Requests are not supported in Community Edition"); | "Branches and Pull Requests are not supported in Community Edition"); | ||||
this.branch.setProperty(branch); | this.branch.setProperty(branch); | ||||
return this; | return this; |
boolean isMain(); | boolean isMain(); | ||||
/** | |||||
* Whether branch has been created through the legacy configuration | |||||
* (scanner parameter sonar.branch) or not | |||||
*/ | |||||
boolean isLegacyFeature(); | |||||
/** | /** | ||||
* Name of the branch | * Name of the branch | ||||
*/ | */ |
@CheckForNull | @CheckForNull | ||||
private BranchImpl createBranch() { | private BranchImpl createBranch() { | ||||
org.sonar.ce.task.projectanalysis.analysis.Branch analysisBranch = analysisMetadataHolder.getBranch(); | org.sonar.ce.task.projectanalysis.analysis.Branch analysisBranch = analysisMetadataHolder.getBranch(); | ||||
if (!analysisBranch.isLegacyFeature()) { | |||||
String branchKey = analysisBranch.getType() == PULL_REQUEST ? analysisBranch.getPullRequestKey() : analysisBranch.getName(); | |||||
return new BranchImpl(analysisBranch.isMain(), branchKey, Branch.Type.valueOf(analysisBranch.getType().name())); | |||||
} | |||||
return null; | |||||
String branchKey = analysisBranch.getType() == PULL_REQUEST ? analysisBranch.getPullRequestKey() : analysisBranch.getName(); | |||||
return new BranchImpl(analysisBranch.isMain(), branchKey, Branch.Type.valueOf(analysisBranch.getType().name())); | |||||
} | } | ||||
private static QualityGate.Status convert(QualityGateStatus status) { | private static QualityGate.Status convert(QualityGateStatus status) { |
import org.sonar.ce.task.projectanalysis.analysis.MutableAnalysisMetadataHolder; | import org.sonar.ce.task.projectanalysis.analysis.MutableAnalysisMetadataHolder; | ||||
import org.sonar.scanner.protocol.output.ScannerReport; | import org.sonar.scanner.protocol.output.ScannerReport; | ||||
import static org.apache.commons.lang.StringUtils.trimToNull; | |||||
import static org.sonar.scanner.protocol.output.ScannerReport.Metadata.BranchType.UNSET; | import static org.sonar.scanner.protocol.output.ScannerReport.Metadata.BranchType.UNSET; | ||||
public class BranchLoader { | public class BranchLoader { | ||||
} | } | ||||
public void load(ScannerReport.Metadata metadata) { | public void load(ScannerReport.Metadata metadata) { | ||||
String deprecatedBranch = trimToNull(metadata.getDeprecatedBranch()); | |||||
String branchName = trimToNull(metadata.getBranchName()); | |||||
if (deprecatedBranch != null && branchName != null) { | |||||
throw MessageException.of("Properties sonar.branch and sonar.branch.name can't be set together"); | |||||
} | |||||
if (delegate == null && hasBranchProperties(metadata)) { | |||||
throw MessageException.of("Current edition does not support branch feature"); | |||||
} | |||||
if (delegate != null && deprecatedBranch == null) { | |||||
if (delegate != null) { | |||||
delegate.load(metadata); | delegate.load(metadata); | ||||
} else if (hasBranchProperties(metadata)) { | |||||
throw MessageException.of("Current edition does not support branch feature"); | |||||
} else { | } else { | ||||
metadataHolder.setBranch(new DefaultBranchImpl(deprecatedBranch)); | |||||
metadataHolder.setBranch(new DefaultBranchImpl()); | |||||
} | } | ||||
} | } | ||||
package org.sonar.ce.task.projectanalysis.component; | package org.sonar.ce.task.projectanalysis.component; | ||||
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.sonar.api.utils.MessageException; | |||||
import org.sonar.ce.task.projectanalysis.analysis.Branch; | import org.sonar.ce.task.projectanalysis.analysis.Branch; | ||||
import org.sonar.core.component.ComponentKeys; | import org.sonar.core.component.ComponentKeys; | ||||
import org.sonar.db.component.BranchDto; | import org.sonar.db.component.BranchDto; | ||||
import org.sonar.db.component.BranchType; | import org.sonar.db.component.BranchType; | ||||
import static java.lang.String.format; | |||||
import static org.apache.commons.lang.StringUtils.isEmpty; | import static org.apache.commons.lang.StringUtils.isEmpty; | ||||
import static org.apache.commons.lang.StringUtils.trimToNull; | import static org.apache.commons.lang.StringUtils.trimToNull; | ||||
/** | /** | ||||
* The default (and legacy) implementation of {@link Branch}. It is used | |||||
* when scanner is configured with parameter "sonar.branch" or when no branch is provided and the branch plugin is not installed. | |||||
* A legacy branch is implemented as a fork of the project, so any branch is considered as "main". | |||||
* Implementation of {@link Branch} for default/main branch. It is used | |||||
* when no branch is provided as a scanner parameter or if the branch plugin is not installed. | |||||
*/ | */ | ||||
public class DefaultBranchImpl implements Branch { | public class DefaultBranchImpl implements Branch { | ||||
private final String branchName; | private final String branchName; | ||||
private final boolean isLegacyBranch; | |||||
public DefaultBranchImpl() { | public DefaultBranchImpl() { | ||||
this(null); | |||||
} | |||||
public DefaultBranchImpl(@Nullable String name) { | |||||
this.isLegacyBranch = (name != null); | |||||
this.branchName = (name == null) ? BranchDto.DEFAULT_MAIN_BRANCH_NAME : name; | |||||
if (!ComponentKeys.isValidLegacyBranch(branchName)) { | |||||
throw MessageException.of(format("\"%s\" is not a valid branch name. " | |||||
+ "Allowed characters are alphanumeric, '-', '_', '.' and '/'.", branchName)); | |||||
} | |||||
this.branchName = BranchDto.DEFAULT_MAIN_BRANCH_NAME; | |||||
} | } | ||||
@Override | @Override | ||||
throw new IllegalStateException("Not valid for the main branch"); | throw new IllegalStateException("Not valid for the main branch"); | ||||
} | } | ||||
@Override | |||||
public boolean isLegacyFeature() { | |||||
return isLegacyBranch; | |||||
} | |||||
@Override | @Override | ||||
public String getName() { | public String getName() { | ||||
return branchName; | return branchName; | ||||
@Override | @Override | ||||
public boolean supportsCrossProjectCpd() { | public boolean supportsCrossProjectCpd() { | ||||
// only on regular project, not on branches | |||||
return !isLegacyBranch; | |||||
return true; | |||||
} | } | ||||
@Override | @Override | ||||
@Override | @Override | ||||
public String generateKey(String projectKey, @Nullable String fileOrDirPath) { | public String generateKey(String projectKey, @Nullable String fileOrDirPath) { | ||||
if (isLegacyBranch) { | |||||
projectKey = ComponentKeys.createKey(projectKey, branchName); | |||||
} | |||||
if (isEmpty(fileOrDirPath)) { | if (isEmpty(fileOrDirPath)) { | ||||
return projectKey; | return projectKey; | ||||
} | } |
Project.Builder builder = new Project.Builder(project.getUuid()) | Project.Builder builder = new Project.Builder(project.getUuid()) | ||||
.setKey(project.getKey()) | .setKey(project.getKey()) | ||||
.setProjectName(project.getName()); | .setProjectName(project.getName()); | ||||
if (!branch.isLegacyFeature() && branch.getType() != PULL_REQUEST && !branch.isMain()) { | |||||
if (branch.getType() != PULL_REQUEST && !branch.isMain()) { | |||||
builder.setBranchName(branch.getName()); | builder.setBranchName(branch.getName()); | ||||
} | } | ||||
return builder.build(); | return builder.build(); |
Branch branch = analysisMetadataHolder.getBranch(); | Branch branch = analysisMetadataHolder.getBranch(); | ||||
// for non-legacy branches, the public key is different from the DB key. | // for non-legacy branches, the public key is different from the DB key. | ||||
if (!branch.isLegacyFeature() && !branch.isMain()) { | |||||
if (!branch.isMain()) { | |||||
return new DefaultBranchImpl(); | return new DefaultBranchImpl(); | ||||
} | } | ||||
return branch; | return branch; |
import java.util.Optional; | import java.util.Optional; | ||||
import javax.annotation.CheckForNull; | import javax.annotation.CheckForNull; | ||||
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.apache.commons.lang.StringUtils; | |||||
import org.sonar.api.utils.MessageException; | import org.sonar.api.utils.MessageException; | ||||
import org.sonar.ce.task.CeTask; | import org.sonar.ce.task.CeTask; | ||||
import org.sonar.ce.task.projectanalysis.analysis.MutableAnalysisMetadataHolder; | import org.sonar.ce.task.projectanalysis.analysis.MutableAnalysisMetadataHolder; | ||||
import org.sonar.ce.task.projectanalysis.batch.BatchReportReader; | import org.sonar.ce.task.projectanalysis.batch.BatchReportReader; | ||||
import org.sonar.ce.task.projectanalysis.component.BranchLoader; | import org.sonar.ce.task.projectanalysis.component.BranchLoader; | ||||
import org.sonar.ce.task.step.ComputationStep; | import org.sonar.ce.task.step.ComputationStep; | ||||
import org.sonar.core.component.ComponentKeys; | |||||
import org.sonar.core.platform.PluginRepository; | import org.sonar.core.platform.PluginRepository; | ||||
import org.sonar.core.util.stream.MoreCollectors; | import org.sonar.core.util.stream.MoreCollectors; | ||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
* @return a {@link Runnable} to execute some checks on the project at the end of the step | * @return a {@link Runnable} to execute some checks on the project at the end of the step | ||||
*/ | */ | ||||
private Runnable loadProject(ScannerReport.Metadata reportMetadata, Organization organization) { | private Runnable loadProject(ScannerReport.Metadata reportMetadata, Organization organization) { | ||||
String reportProjectKey = projectKeyFromReport(reportMetadata); | |||||
CeTask.Component mainComponent = mandatoryComponent(ceTask.getMainComponent()); | CeTask.Component mainComponent = mandatoryComponent(ceTask.getMainComponent()); | ||||
String mainComponentKey = mainComponent.getKey() | String mainComponentKey = mainComponent.getKey() | ||||
.orElseThrow(() -> MessageException.of(format( | .orElseThrow(() -> MessageException.of(format( | ||||
.orElseThrow(() -> MessageException.of(format( | .orElseThrow(() -> MessageException.of(format( | ||||
"Compute Engine task component key is null. Project with UUID %s must have been deleted since report was uploaded. Can not proceed.", | "Compute Engine task component key is null. Project with UUID %s must have been deleted since report was uploaded. Can not proceed.", | ||||
component.getUuid()))); | component.getUuid()))); | ||||
ComponentDto dto = toProject(reportProjectKey); | |||||
ComponentDto dto = toProject(reportMetadata.getProjectKey()); | |||||
analysisMetadata.setProject(Project.from(dto)); | analysisMetadata.setProject(Project.from(dto)); | ||||
return () -> { | return () -> { | ||||
if (!mainComponentKey.equals(reportProjectKey)) { | |||||
if (!mainComponentKey.equals(reportMetadata.getProjectKey())) { | |||||
throw MessageException.of(format( | throw MessageException.of(format( | ||||
"ProjectKey in report (%s) is not consistent with projectKey under which the report has been submitted (%s)", | "ProjectKey in report (%s) is not consistent with projectKey under which the report has been submitted (%s)", | ||||
reportProjectKey, | |||||
reportMetadata.getProjectKey(), | |||||
mainComponentKey)); | mainComponentKey)); | ||||
} | } | ||||
if (!dto.getOrganizationUuid().equals(organization.getUuid())) { | if (!dto.getOrganizationUuid().equals(organization.getUuid())) { | ||||
} | } | ||||
} | } | ||||
private static String projectKeyFromReport(ScannerReport.Metadata reportMetadata) { | |||||
String deprecatedBranch = reportMetadata.getDeprecatedBranch(); | |||||
if (StringUtils.isNotEmpty(deprecatedBranch)) { | |||||
return ComponentKeys.createKey(reportMetadata.getProjectKey(), deprecatedBranch); | |||||
} | |||||
return reportMetadata.getProjectKey(); | |||||
} | |||||
@Override | @Override | ||||
public String getDescription() { | public String getDescription() { | ||||
return "Load analysis metadata"; | return "Load analysis metadata"; |
public void set_branch() { | public void set_branch() { | ||||
AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(editionProvider); | AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(editionProvider); | ||||
underTest.setBranch(new DefaultBranchImpl("master")); | |||||
underTest.setBranch(new DefaultBranchImpl()); | |||||
assertThat(underTest.getBranch().getName()).isEqualTo("master"); | assertThat(underTest.getBranch().getName()).isEqualTo("master"); | ||||
} | } | ||||
@Test | @Test | ||||
public void setBranch_throws_ISE_when_called_twice() { | public void setBranch_throws_ISE_when_called_twice() { | ||||
AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(editionProvider); | AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(editionProvider); | ||||
underTest.setBranch(new DefaultBranchImpl("master")); | |||||
underTest.setBranch(new DefaultBranchImpl()); | |||||
expectedException.expect(IllegalStateException.class); | expectedException.expect(IllegalStateException.class); | ||||
expectedException.expectMessage("Branch has already been set"); | expectedException.expectMessage("Branch has already been set"); | ||||
underTest.setBranch(new DefaultBranchImpl("master")); | |||||
underTest.setBranch(new DefaultBranchImpl()); | |||||
} | } | ||||
@Test | @Test | ||||
when(editionProvider.get()).thenReturn(Optional.ofNullable(edition)); | when(editionProvider.get()).thenReturn(Optional.ofNullable(edition)); | ||||
Branch branch = mock(Branch.class); | Branch branch = mock(Branch.class); | ||||
when(branch.isMain()).thenReturn(true); | when(branch.isMain()).thenReturn(true); | ||||
when(branch.isLegacyFeature()).thenReturn(false); | |||||
AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(editionProvider); | |||||
underTest.setBranch(branch); | |||||
assertThat(underTest.getBranch()).isSameAs(branch); | |||||
} | |||||
@Test | |||||
@UseDataProvider("anyEditionIncludingNone") | |||||
public void setBranch_does_not_fail_if_legacy_branch_on_any_edition(@Nullable Edition edition) { | |||||
when(editionProvider.get()).thenReturn(Optional.ofNullable(edition)); | |||||
Branch branch = mock(Branch.class); | |||||
when(branch.isMain()).thenReturn(false); | |||||
when(branch.isLegacyFeature()).thenReturn(true); | |||||
AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(editionProvider); | AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(editionProvider); | ||||
underTest.setBranch(branch); | underTest.setBranch(branch); | ||||
@Test | @Test | ||||
@UseDataProvider("anyEditionIncludingNoneButCommunity") | @UseDataProvider("anyEditionIncludingNoneButCommunity") | ||||
public void setBranch_does_not_fail_if_non_main_non_legacy_branch_on_any_edition_but_Community(@Nullable Edition edition) { | |||||
public void setBranch_does_not_fail_if_non_main_on_any_edition_but_Community(@Nullable Edition edition) { | |||||
when(editionProvider.get()).thenReturn(Optional.ofNullable(edition)); | when(editionProvider.get()).thenReturn(Optional.ofNullable(edition)); | ||||
Branch branch = mock(Branch.class); | Branch branch = mock(Branch.class); | ||||
when(branch.isMain()).thenReturn(false); | when(branch.isMain()).thenReturn(false); | ||||
when(branch.isLegacyFeature()).thenReturn(false); | |||||
AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(editionProvider); | AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(editionProvider); | ||||
underTest.setBranch(branch); | underTest.setBranch(branch); | ||||
} | } | ||||
@Test | @Test | ||||
public void setBranch_fails_if_non_main_non_legacy_branch_on_Community_edition() { | |||||
public void setBranch_fails_if_non_main_branch_on_Community_edition() { | |||||
when(editionProvider.get()).thenReturn(Optional.of(Edition.COMMUNITY)); | when(editionProvider.get()).thenReturn(Optional.of(Edition.COMMUNITY)); | ||||
Branch branch = mock(Branch.class); | Branch branch = mock(Branch.class); | ||||
when(branch.isMain()).thenReturn(false); | when(branch.isMain()).thenReturn(false); | ||||
when(branch.isLegacyFeature()).thenReturn(false); | |||||
AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(editionProvider); | AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(editionProvider); | ||||
expectedException.expect(IllegalStateException.class); | expectedException.expect(IllegalStateException.class); |
assertThat(config.get("sonar.leak.period")).hasValue("1"); | assertThat(config.get("sonar.leak.period")).hasValue("1"); | ||||
} | } | ||||
@Test | |||||
public void legacy_branch() { | |||||
ComponentDto project = db.components().insertMainBranch(); | |||||
db.properties().insertProperties(newComponentPropertyDto(project).setKey("sonar.leak.period").setValue("1")); | |||||
Branch branch = createBranch("legacy", true); | |||||
when(branch.isLegacyFeature()).thenReturn(true); | |||||
Configuration config = underTest.newProjectConfiguration(project.getKey(), createBranch(branch.getName(), true)); | |||||
assertThat(config.get("sonar.leak.period")).hasValue("1"); | |||||
} | |||||
private static Branch createBranch(String name, boolean isMain) { | private static Branch createBranch(String name, boolean isMain) { | ||||
Branch branch = mock(Branch.class); | Branch branch = mock(Branch.class); | ||||
when(branch.getName()).thenReturn(name); | when(branch.getName()).thenReturn(name); |
import org.sonar.ce.task.projectanalysis.analysis.Branch; | import org.sonar.ce.task.projectanalysis.analysis.Branch; | ||||
import org.sonar.ce.task.projectanalysis.analysis.Organization; | import org.sonar.ce.task.projectanalysis.analysis.Organization; | ||||
import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule; | import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule; | ||||
import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl; | |||||
import org.sonar.ce.task.projectanalysis.metric.Metric; | import org.sonar.ce.task.projectanalysis.metric.Metric; | ||||
import org.sonar.ce.task.projectanalysis.qualitygate.Condition; | import org.sonar.ce.task.projectanalysis.qualitygate.Condition; | ||||
import org.sonar.ce.task.projectanalysis.qualitygate.ConditionStatus; | import org.sonar.ce.task.projectanalysis.qualitygate.ConditionStatus; | ||||
assertThat(taskContextCaptor.getValue().getProjectAnalysis().getAnalysis()).isEmpty(); | assertThat(taskContextCaptor.getValue().getProjectAnalysis().getAnalysis()).isEmpty(); | ||||
} | } | ||||
@Test | |||||
public void branch_is_empty_when_legacy_branch_implementation_is_used() { | |||||
analysisMetadataHolder.setBranch(new DefaultBranchImpl("feature/foo")); | |||||
underTest.finished(true); | |||||
verify(postProjectAnalysisTask).finished(taskContextCaptor.capture()); | |||||
assertThat(taskContextCaptor.getValue().getProjectAnalysis().getBranch()).isEmpty(); | |||||
} | |||||
@Test | @Test | ||||
public void branch_comes_from_AnalysisMetadataHolder_when_set() { | public void branch_comes_from_AnalysisMetadataHolder_when_set() { | ||||
analysisMetadataHolder.setBranch(new Branch() { | analysisMetadataHolder.setBranch(new Branch() { | ||||
return false; | return false; | ||||
} | } | ||||
@Override | |||||
public boolean isLegacyFeature() { | |||||
return false; | |||||
} | |||||
@Override | @Override | ||||
public String getMergeBranchUuid() { | public String getMergeBranchUuid() { | ||||
throw new UnsupportedOperationException(); | throw new UnsupportedOperationException(); | ||||
new PostProjectAnalysisTasksExecutor( | new PostProjectAnalysisTasksExecutor( | ||||
ceTask, analysisMetadataHolder, qualityGateHolder, qualityGateStatusHolder, reportReader, | ceTask, analysisMetadataHolder, qualityGateHolder, qualityGateStatusHolder, reportReader, | ||||
system2, new PostProjectAnalysisTask[] {logStatisticsTask}) | system2, new PostProjectAnalysisTask[] {logStatisticsTask}) | ||||
.finished(allStepsExecuted); | |||||
.finished(allStepsExecuted); | |||||
verify(logStatisticsTask).finished(taskContextCaptor.capture()); | verify(logStatisticsTask).finished(taskContextCaptor.capture()); | ||||
List<String> logs = logTester.logs(LoggerLevel.INFO); | List<String> logs = logTester.logs(LoggerLevel.INFO); | ||||
assertThat(logs).hasSize(1); | assertThat(logs).hasSize(1); | ||||
StringBuilder expectedLog = new StringBuilder("^PT1 "); | StringBuilder expectedLog = new StringBuilder("^PT1 "); | ||||
stats.forEach((k,v) -> expectedLog.append("\\| " + k + "=" + v +" ")); | |||||
stats.forEach((k, v) -> expectedLog.append("\\| " + k + "=" + v + " ")); | |||||
expectedLog.append("\\| status=SUCCESS \\| time=\\d+ms$"); | expectedLog.append("\\| status=SUCCESS \\| time=\\d+ms$"); | ||||
assertThat(logs.get(0)).matches(expectedLog.toString()); | assertThat(logs.get(0)).matches(expectedLog.toString()); | ||||
} | } |
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||
import static org.mockito.Mockito.times; | |||||
import static org.mockito.Mockito.verify; | |||||
public class BranchLoaderTest { | public class BranchLoaderTest { | ||||
@Rule | @Rule | ||||
public AnalysisMetadataHolderRule metadataHolder = new AnalysisMetadataHolderRule(); | public AnalysisMetadataHolderRule metadataHolder = new AnalysisMetadataHolderRule(); | ||||
@Test | @Test | ||||
public void throw_ME_if_both_branch_properties_are_set() { | |||||
public void throw_ME_if_both_delegate_absent_and_has_branch_parameters() { | |||||
ScannerReport.Metadata metadata = ScannerReport.Metadata.newBuilder() | ScannerReport.Metadata metadata = ScannerReport.Metadata.newBuilder() | ||||
.setDeprecatedBranch("foo") | |||||
.setBranchName("bar") | .setBranchName("bar") | ||||
.build(); | .build(); | ||||
expectedException.expect(MessageException.class); | expectedException.expect(MessageException.class); | ||||
expectedException.expectMessage("Properties sonar.branch and sonar.branch.name can't be set together"); | |||||
expectedException.expectMessage("Current edition does not support branch feature"); | |||||
new BranchLoader(metadataHolder).load(metadata); | new BranchLoader(metadataHolder).load(metadata); | ||||
} | } | ||||
} | } | ||||
@Test | @Test | ||||
public void default_support_of_branches_is_enabled_if_delegate_is_absent() { | |||||
public void default_support_of_branches_is_enabled_if_delegate_is_present_for_main_branch() { | |||||
ScannerReport.Metadata metadata = ScannerReport.Metadata.newBuilder() | ScannerReport.Metadata metadata = ScannerReport.Metadata.newBuilder() | ||||
.setDeprecatedBranch("foo") | |||||
.build(); | .build(); | ||||
BranchLoaderDelegate delegate = mock(BranchLoaderDelegate.class); | |||||
new BranchLoader(metadataHolder).load(metadata); | |||||
assertThat(metadataHolder.getBranch()).isNotNull(); | |||||
Branch branch = metadataHolder.getBranch(); | |||||
assertThat(branch.isMain()).isTrue(); | |||||
assertThat(branch.getName()).isEqualTo("foo"); | |||||
} | |||||
@Test | |||||
public void default_support_of_branches_is_enabled_if_delegate_is_present() { | |||||
ScannerReport.Metadata metadata = ScannerReport.Metadata.newBuilder() | |||||
.setDeprecatedBranch("foo") | |||||
.build(); | |||||
FakeDelegate delegate = new FakeDelegate(); | |||||
new BranchLoader(metadataHolder, delegate).load(metadata); | new BranchLoader(metadataHolder, delegate).load(metadata); | ||||
assertThat(metadataHolder.getBranch()).isNotNull(); | |||||
Branch branch = metadataHolder.getBranch(); | |||||
assertThat(branch.isMain()).isTrue(); | |||||
assertThat(branch.getName()).isEqualTo("foo"); | |||||
} | |||||
private class FakeDelegate implements BranchLoaderDelegate { | |||||
Branch branch = mock(Branch.class); | |||||
@Override | |||||
public void load(ScannerReport.Metadata metadata) { | |||||
metadataHolder.setBranch(branch); | |||||
} | |||||
verify(delegate, times(1)).load(metadata); | |||||
} | } | ||||
} | } |
*/ | */ | ||||
package org.sonar.ce.task.projectanalysis.component; | package org.sonar.ce.task.projectanalysis.component; | ||||
import javax.annotation.Nullable; | |||||
import org.junit.Rule; | import org.junit.Rule; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.junit.rules.ExpectedException; | import org.junit.rules.ExpectedException; | ||||
import org.sonar.api.utils.MessageException; | |||||
import org.sonar.db.component.BranchDto; | import org.sonar.db.component.BranchDto; | ||||
import org.sonar.db.component.BranchType; | import org.sonar.db.component.BranchType; | ||||
import org.sonar.scanner.protocol.output.ScannerReport; | import org.sonar.scanner.protocol.output.ScannerReport; | ||||
@Rule | @Rule | ||||
public ExpectedException expectedException = ExpectedException.none(); | public ExpectedException expectedException = ExpectedException.none(); | ||||
@Test | |||||
public void throw_ME_if_name_contains_invalid_characters() { | |||||
assertThatNameIsCorrect("master"); | |||||
assertThatNameIsCorrect("feature/foo"); | |||||
assertThatNameIsCorrect("feature_foo"); | |||||
assertThatNameIsNotCorrect("feature foo"); | |||||
assertThatNameIsNotCorrect("feature#foo"); | |||||
} | |||||
@Test | @Test | ||||
public void default_branch_represents_the_project() { | public void default_branch_represents_the_project() { | ||||
DefaultBranchImpl branch = new DefaultBranchImpl(); | DefaultBranchImpl branch = new DefaultBranchImpl(); | ||||
assertThat(branch.generateKey(PROJECT_KEY, null)).isEqualTo("P"); | assertThat(branch.generateKey(PROJECT_KEY, null)).isEqualTo("P"); | ||||
assertThat(branch.generateKey(PROJECT_KEY, FILE.getProjectRelativePath())).isEqualTo("P:src/Foo.js"); | assertThat(branch.generateKey(PROJECT_KEY, FILE.getProjectRelativePath())).isEqualTo("P:src/Foo.js"); | ||||
} | } | ||||
@Test | |||||
public void branch_represents_a_forked_project_with_different_key() { | |||||
DefaultBranchImpl branch = new DefaultBranchImpl("bar"); | |||||
// not a real branch. Parameter sonar.branch forks project. | |||||
assertThat(branch.isMain()).isTrue(); | |||||
assertThat(branch.getType()).isEqualTo(BranchType.LONG); | |||||
assertThat(branch.getName()).isEqualTo("bar"); | |||||
assertThat(branch.supportsCrossProjectCpd()).isFalse(); | |||||
assertThat(branch.generateKey(PROJECT_KEY, null)).isEqualTo("P:bar"); | |||||
assertThat(branch.generateKey(PROJECT_KEY, FILE.getProjectRelativePath())).isEqualTo("P:bar:src/Foo.js"); | |||||
} | |||||
private void assertThatNameIsCorrect(@Nullable String name) { | |||||
DefaultBranchImpl branch = new DefaultBranchImpl(name); | |||||
assertThat(branch.getName()).isEqualTo(name); | |||||
} | |||||
private void assertThatNameIsNotCorrect(String name) { | |||||
expectedException.expect(MessageException.class); | |||||
expectedException.expectMessage("\"" + name + "\" is not a valid branch name. Allowed characters are alphanumeric, '-', '_', '.' and '/'."); | |||||
new DefaultBranchImpl(name); | |||||
} | |||||
} | } |
public static Object[][] noBranchNameBranches() { | public static Object[][] noBranchNameBranches() { | ||||
Branch mainBranch = mock(Branch.class); | Branch mainBranch = mock(Branch.class); | ||||
when(mainBranch.isMain()).thenReturn(true); | when(mainBranch.isMain()).thenReturn(true); | ||||
when(mainBranch.isLegacyFeature()).thenReturn(false); | |||||
when(mainBranch.getType()).thenReturn(BranchType.LONG); | when(mainBranch.getType()).thenReturn(BranchType.LONG); | ||||
Branch legacyBranch = mock(Branch.class); | |||||
when(legacyBranch.isLegacyFeature()).thenReturn(true); | |||||
Branch shortBranch = mock(Branch.class); | Branch shortBranch = mock(Branch.class); | ||||
when(shortBranch.isLegacyFeature()).thenReturn(false); | |||||
when(shortBranch.isMain()).thenReturn(false); | when(shortBranch.isMain()).thenReturn(false); | ||||
when(shortBranch.getType()).thenReturn(BranchType.SHORT); | when(shortBranch.getType()).thenReturn(BranchType.SHORT); | ||||
Branch pr = mock(Branch.class); | Branch pr = mock(Branch.class); | ||||
when(pr.isLegacyFeature()).thenReturn(false); | |||||
when(pr.isMain()).thenReturn(false); | when(pr.isMain()).thenReturn(false); | ||||
when(pr.getType()).thenReturn(BranchType.PULL_REQUEST); | when(pr.getType()).thenReturn(BranchType.PULL_REQUEST); | ||||
return new Object[][] { | return new Object[][] { | ||||
{mainBranch}, | {mainBranch}, | ||||
{legacyBranch}, | |||||
{shortBranch}, | {shortBranch}, | ||||
{pr} | {pr} | ||||
}; | }; | ||||
expectedException.expect(IllegalStateException.class); | expectedException.expect(IllegalStateException.class); | ||||
expectedException.expectMessage("Can not find DTO for assignee uuid " + assigneeUuid); | expectedException.expectMessage("Can not find DTO for assignee uuid " + assigneeUuid); | ||||
underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid); | underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid); | ||||
} | } | ||||
assertThat(changedIssue.getAssignee()).isEmpty(); | assertThat(changedIssue.getAssignee()).isEmpty(); | ||||
assertThat(changedIssue.getRule().getKey()).isEqualTo(issue.ruleKey()); | assertThat(changedIssue.getRule().getKey()).isEqualTo(issue.ruleKey()); | ||||
assertThat(changedIssue.getRule().getName()).isEqualTo(ruleRepository.getByKey(issue.ruleKey()).getName()); | assertThat(changedIssue.getRule().getName()).isEqualTo(ruleRepository.getByKey(issue.ruleKey()).getName()); | ||||
} | |||||
); | |||||
}); | |||||
} | } | ||||
private static Map<String, UserDto> nonEmptyAssigneesByUuid() { | private static Map<String, UserDto> nonEmptyAssigneesByUuid() { | ||||
private static Branch newBranch(BranchType branchType, String branchName) { | private static Branch newBranch(BranchType branchType, String branchName) { | ||||
Branch longBranch = mock(Branch.class); | Branch longBranch = mock(Branch.class); | ||||
when(longBranch.isLegacyFeature()).thenReturn(false); | |||||
when(longBranch.isMain()).thenReturn(false); | when(longBranch.isMain()).thenReturn(false); | ||||
when(longBranch.getType()).thenReturn(branchType); | when(longBranch.getType()).thenReturn(branchType); | ||||
when(longBranch.getName()).thenReturn(branchName); | when(longBranch.getName()).thenReturn(branchName); |
Branch branch = mock(Branch.class); | Branch branch = mock(Branch.class); | ||||
when(branch.getName()).thenReturn("origin/feature"); | when(branch.getName()).thenReturn("origin/feature"); | ||||
when(branch.isMain()).thenReturn(false); | when(branch.isMain()).thenReturn(false); | ||||
when(branch.isLegacyFeature()).thenReturn(false); | |||||
when(branch.generateKey(any(), any())).thenReturn("generated"); | when(branch.generateKey(any(), any())).thenReturn("generated"); | ||||
analysisMetadataHolder.setRootComponentRef(ROOT_REF) | analysisMetadataHolder.setRootComponentRef(ROOT_REF) | ||||
.setAnalysisDate(ANALYSIS_DATE) | .setAnalysisDate(ANALYSIS_DATE) | ||||
Branch branch = mock(Branch.class); | Branch branch = mock(Branch.class); | ||||
when(branch.getName()).thenReturn(branchDto.getBranch()); | when(branch.getName()).thenReturn(branchDto.getBranch()); | ||||
when(branch.isMain()).thenReturn(false); | when(branch.isMain()).thenReturn(false); | ||||
when(branch.isLegacyFeature()).thenReturn(false); | |||||
when(branch.generateKey(any(), any())).thenReturn(branchDto.getDbKey()); | when(branch.generateKey(any(), any())).thenReturn(branchDto.getDbKey()); | ||||
analysisMetadataHolder.setRootComponentRef(ROOT_REF) | analysisMetadataHolder.setRootComponentRef(ROOT_REF) | ||||
.setAnalysisDate(ANALYSIS_DATE) | .setAnalysisDate(ANALYSIS_DATE) | ||||
verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_FILE_NAME_1, null); | verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_PROJECT_KEY + ":" + REPORT_FILE_PATH_1, REPORT_FILE_NAME_1, null); | ||||
} | } | ||||
@Test | |||||
public void generate_keys_when_using_legacy_branch() { | |||||
analysisMetadataHolder.setRootComponentRef(ROOT_REF) | |||||
.setAnalysisDate(ANALYSIS_DATE) | |||||
.setProject(Project.from(newPrivateProjectDto(newOrganizationDto()).setDbKey(REPORT_PROJECT_KEY))) | |||||
.setBranch(new DefaultBranchImpl("origin/feature")); | |||||
BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, reportModulesPath); | |||||
reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, FILE_1_REF)); | |||||
reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_PATH_1)); | |||||
underTest.execute(new TestComputationStepContext()); | |||||
verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY + ":origin/feature", analysisMetadataHolder.getProject().getName(), null); | |||||
verifyComponentByKey(REPORT_PROJECT_KEY + ":origin/feature:" + REPORT_DIR_PATH_1, REPORT_DIR_PATH_1); | |||||
verifyComponentByRef(FILE_1_REF, REPORT_PROJECT_KEY + ":origin/feature:" + REPORT_FILE_PATH_1, REPORT_FILE_NAME_1, null); | |||||
} | |||||
@Test | @Test | ||||
public void compute_keys_and_uuids_on_project_having_module_and_directory() { | public void compute_keys_and_uuids_on_project_having_module_and_directory() { | ||||
setAnalysisMetadataHolder(); | setAnalysisMetadataHolder(); | ||||
private void setAnalysisMetadataHolder() { | private void setAnalysisMetadataHolder() { | ||||
analysisMetadataHolder.setRootComponentRef(ROOT_REF) | analysisMetadataHolder.setRootComponentRef(ROOT_REF) | ||||
.setAnalysisDate(ANALYSIS_DATE) | .setAnalysisDate(ANALYSIS_DATE) | ||||
.setBranch(new DefaultBranchImpl(null)) | |||||
.setBranch(new DefaultBranchImpl()) | |||||
.setProject(Project.from(newPrivateProjectDto(newOrganizationDto()).setDbKey(REPORT_PROJECT_KEY))); | .setProject(Project.from(newPrivateProjectDto(newOrganizationDto()).setDbKey(REPORT_PROJECT_KEY))); | ||||
} | } | ||||
reset(measureRepository, eventRepository, notificationService); | reset(measureRepository, eventRepository, notificationService); | ||||
} | } | ||||
@Test | |||||
public void verify_branch_name_is_set_in_notification_when_not_main() { | |||||
String branchName = "feature1"; | |||||
analysisMetadataHolder.setBranch(new DefaultBranchImpl(branchName) { | |||||
@Override | |||||
public boolean isMain() { | |||||
return false; | |||||
} | |||||
}); | |||||
when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)) | |||||
.thenReturn(of(Measure.newMeasureBuilder().setQualityGateStatus(OK_QUALITY_GATE_STATUS).createNoValue())); | |||||
when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn( | |||||
of(Measure.newMeasureBuilder().setQualityGateStatus(new QualityGateStatus(ERROR)).createNoValue())); | |||||
underTest.execute(new TestComputationStepContext()); | |||||
verify(notificationService).deliver(notificationArgumentCaptor.capture()); | |||||
Notification notification = notificationArgumentCaptor.getValue(); | |||||
assertThat(notification.getType()).isEqualTo("alerts"); | |||||
assertThat(notification.getFieldValue("projectKey")).isEqualTo(PROJECT_COMPONENT.getKey()); | |||||
assertThat(notification.getFieldValue("projectName")).isEqualTo(PROJECT_COMPONENT.getName()); | |||||
assertThat(notification.getFieldValue("projectVersion")).isEqualTo(PROJECT_COMPONENT.getProjectAttributes().getProjectVersion()); | |||||
assertThat(notification.getFieldValue("branch")).isEqualTo(branchName); | |||||
reset(measureRepository, eventRepository, notificationService); | |||||
} | |||||
@Test | @Test | ||||
public void verify_branch_name_is_not_set_in_notification_when_main() { | public void verify_branch_name_is_not_set_in_notification_when_main() { | ||||
analysisMetadataHolder.setBranch(new DefaultBranchImpl()); | analysisMetadataHolder.setBranch(new DefaultBranchImpl()); |
throw new UnsupportedOperationException(); | throw new UnsupportedOperationException(); | ||||
} | } | ||||
@Override | |||||
public boolean isLegacyFeature() { | |||||
return false; | |||||
} | |||||
@Override | @Override | ||||
public String getName() { | public String getName() { | ||||
return name; | return name; |
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import javax.annotation.concurrent.Immutable; | import javax.annotation.concurrent.Immutable; | ||||
import org.sonar.api.server.ServerSide; | import org.sonar.api.server.ServerSide; | ||||
import org.sonar.core.component.ComponentKeys; | |||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.component.BranchDto; | import org.sonar.db.component.BranchDto; | ||||
import org.sonar.db.component.BranchType; | import org.sonar.db.component.BranchType; | ||||
import org.sonar.db.component.ComponentDto; | import org.sonar.db.component.ComponentDto; | ||||
import org.sonar.db.organization.OrganizationDto; | import org.sonar.db.organization.OrganizationDto; | ||||
import static com.google.common.base.Preconditions.checkArgument; | |||||
import static com.google.common.base.Preconditions.checkState; | import static com.google.common.base.Preconditions.checkState; | ||||
import static java.util.Objects.requireNonNull; | import static java.util.Objects.requireNonNull; | ||||
/** | /** | ||||
* Branch code for {@link ReportSubmitter}. | * Branch code for {@link ReportSubmitter}. | ||||
* <p> | * <p> | ||||
* Does not support branches (except deprecated branch feature provided by "sonar.branch") unless an implementation of | |||||
* {@link BranchSupportDelegate} is available. | |||||
* Does not support branches unless an implementation of {@link BranchSupportDelegate} is available. | |||||
*/ | */ | ||||
@ServerSide | @ServerSide | ||||
public class BranchSupport { | public class BranchSupport { | ||||
this.delegate = delegate; | this.delegate = delegate; | ||||
} | } | ||||
ComponentKey createComponentKey(String projectKey, @Nullable String deprecatedBranch, Map<String, String> characteristics) { | |||||
ComponentKey createComponentKey(String projectKey, Map<String, String> characteristics) { | |||||
if (characteristics.isEmpty()) { | if (characteristics.isEmpty()) { | ||||
return new ComponentKeyImpl(projectKey, deprecatedBranch, ComponentKeys.createKey(projectKey, deprecatedBranch)); | |||||
return new ComponentKeyImpl(projectKey, projectKey); | |||||
} else { | } else { | ||||
checkState(delegate != null, "Current edition does not support branch feature"); | checkState(delegate != null, "Current edition does not support branch feature"); | ||||
} | } | ||||
checkArgument(deprecatedBranch == null, "Deprecated branch feature can't be used at the same time as new branch support"); | |||||
return delegate.createComponentKey(projectKey, characteristics); | return delegate.createComponentKey(projectKey, characteristics); | ||||
} | } | ||||
public abstract String getDbKey(); | public abstract String getDbKey(); | ||||
public abstract Optional<String> getDeprecatedBranchName(); | |||||
public abstract Optional<Branch> getBranch(); | public abstract Optional<Branch> getBranch(); | ||||
public abstract Optional<String> getPullRequestKey(); | public abstract Optional<String> getPullRequestKey(); | ||||
public final boolean isDeprecatedBranch() { | |||||
return getDeprecatedBranchName().isPresent(); | |||||
} | |||||
public final boolean isMainBranch() { | public final boolean isMainBranch() { | ||||
return !getBranch().isPresent() && !getPullRequestKey().isPresent(); | return !getBranch().isPresent() && !getPullRequestKey().isPresent(); | ||||
} | } | ||||
private static final class ComponentKeyImpl extends ComponentKey { | private static final class ComponentKeyImpl extends ComponentKey { | ||||
private final String key; | private final String key; | ||||
private final String dbKey; | private final String dbKey; | ||||
@CheckForNull | |||||
private final String deprecatedBranchName; | |||||
public ComponentKeyImpl(String key, @Nullable String deprecatedBranchName, String dbKey) { | |||||
public ComponentKeyImpl(String key, String dbKey) { | |||||
this.key = key; | this.key = key; | ||||
this.deprecatedBranchName = deprecatedBranchName; | |||||
this.dbKey = dbKey; | this.dbKey = dbKey; | ||||
} | } | ||||
return dbKey; | return dbKey; | ||||
} | } | ||||
@Override | |||||
public Optional<String> getDeprecatedBranchName() { | |||||
return Optional.ofNullable(deprecatedBranchName); | |||||
} | |||||
@Override | @Override | ||||
public Optional<Branch> getBranch() { | public Optional<Branch> getBranch() { | ||||
return Optional.empty(); | return Optional.empty(); | ||||
} | } | ||||
ComponentKeyImpl that = (ComponentKeyImpl) o; | ComponentKeyImpl that = (ComponentKeyImpl) o; | ||||
return Objects.equals(key, that.key) && | return Objects.equals(key, that.key) && | ||||
Objects.equals(dbKey, that.dbKey) && | |||||
Objects.equals(deprecatedBranchName, that.deprecatedBranchName); | |||||
Objects.equals(dbKey, that.dbKey); | |||||
} | } | ||||
@Override | @Override | ||||
public int hashCode() { | public int hashCode() { | ||||
return Objects.hash(key, dbKey, deprecatedBranchName); | |||||
return Objects.hash(key, dbKey); | |||||
} | } | ||||
} | } | ||||
} | } |
* @throws NotFoundException if the organization with the specified key does not exist | * @throws NotFoundException if the organization with the specified key does not exist | ||||
* @throws IllegalArgumentException if the organization with the specified key is not the organization of the specified project (when it already exists in DB) | * @throws IllegalArgumentException if the organization with the specified key is not the organization of the specified project (when it already exists in DB) | ||||
*/ | */ | ||||
public CeTask submit(String organizationKey, String projectKey, @Nullable String deprecatedBranch, @Nullable String projectName, | |||||
public CeTask submit(String organizationKey, String projectKey, @Nullable String projectName, | |||||
Map<String, String> characteristics, InputStream reportInput) { | Map<String, String> characteristics, InputStream reportInput) { | ||||
try (DbSession dbSession = dbClient.openSession(false)) { | try (DbSession dbSession = dbClient.openSession(false)) { | ||||
OrganizationDto organizationDto = getOrganizationDtoOrFail(dbSession, organizationKey); | OrganizationDto organizationDto = getOrganizationDtoOrFail(dbSession, organizationKey); | ||||
BranchSupport.ComponentKey componentKey = branchSupport.createComponentKey(projectKey, deprecatedBranch, characteristics); | |||||
BranchSupport.ComponentKey componentKey = branchSupport.createComponentKey(projectKey, characteristics); | |||||
Optional<ComponentDto> existingComponent = dbClient.componentDao().selectByKey(dbSession, componentKey.getDbKey()); | Optional<ComponentDto> existingComponent = dbClient.componentDao().selectByKey(dbSession, componentKey.getDbKey()); | ||||
validateProject(dbSession, existingComponent, projectKey); | validateProject(dbSession, existingComponent, projectKey); | ||||
ensureOrganizationIsConsistent(existingComponent, organizationDto); | ensureOrganizationIsConsistent(existingComponent, organizationDto); | ||||
} | } | ||||
private ComponentDto createComponent(DbSession dbSession, OrganizationDto organization, BranchSupport.ComponentKey componentKey, @Nullable String projectName) { | private ComponentDto createComponent(DbSession dbSession, OrganizationDto organization, BranchSupport.ComponentKey componentKey, @Nullable String projectName) { | ||||
if (componentKey.isMainBranch() || componentKey.isDeprecatedBranch()) { | |||||
if (componentKey.isMainBranch()) { | |||||
ComponentDto project = createProject(dbSession, organization, componentKey, projectName); | ComponentDto project = createProject(dbSession, organization, componentKey, projectName); | ||||
componentUpdater.commitAndIndex(dbSession, project); | componentUpdater.commitAndIndex(dbSession, project); | ||||
return project; | return project; | ||||
.setOrganizationUuid(organization.getUuid()) | .setOrganizationUuid(organization.getUuid()) | ||||
.setKey(componentKey.getKey()) | .setKey(componentKey.getKey()) | ||||
.setName(defaultIfBlank(projectName, componentKey.getKey())) | .setName(defaultIfBlank(projectName, componentKey.getKey())) | ||||
.setDeprecatedBranch(componentKey.getDeprecatedBranchName().orElse(null)) | |||||
.setQualifier(Qualifiers.PROJECT) | .setQualifier(Qualifiers.PROJECT) | ||||
.setPrivate(newProjectPrivate) | .setPrivate(newProjectPrivate) | ||||
.build(); | .build(); |
private static final String PARAM_ORGANIZATION_KEY = "organization"; | private static final String PARAM_ORGANIZATION_KEY = "organization"; | ||||
private static final String PARAM_PROJECT_KEY = "projectKey"; | private static final String PARAM_PROJECT_KEY = "projectKey"; | ||||
private static final String PARAM_PROJECT_BRANCH = "projectBranch"; | |||||
private static final String PARAM_PROJECT_NAME = "projectName"; | private static final String PARAM_PROJECT_NAME = "projectName"; | ||||
private static final String PARAM_REPORT_DATA = "report"; | private static final String PARAM_REPORT_DATA = "report"; | ||||
private static final String PARAM_ANALYSIS_CHARACTERISTIC = "characteristic"; | private static final String PARAM_ANALYSIS_CHARACTERISTIC = "characteristic"; | ||||
.setDescription("Key of project") | .setDescription("Key of project") | ||||
.setExampleValue("my_project"); | .setExampleValue("my_project"); | ||||
// deprecated branch (see scanner parameter sonar.branch) | |||||
action | |||||
.createParam(PARAM_PROJECT_BRANCH) | |||||
.setDescription("Optional branch of project") | |||||
.setExampleValue("branch-1.x"); | |||||
action | action | ||||
.createParam(PARAM_PROJECT_NAME) | .createParam(PARAM_PROJECT_NAME) | ||||
.setRequired(false) | .setRequired(false) | ||||
.emptyAsNull() | .emptyAsNull() | ||||
.or(defaultOrganizationProvider.get()::getKey); | .or(defaultOrganizationProvider.get()::getKey); | ||||
String projectKey = wsRequest.mandatoryParam(PARAM_PROJECT_KEY); | String projectKey = wsRequest.mandatoryParam(PARAM_PROJECT_KEY); | ||||
String deprecatedBranch = wsRequest.param(PARAM_PROJECT_BRANCH); | |||||
String projectName = abbreviate(defaultIfBlank(wsRequest.param(PARAM_PROJECT_NAME), projectKey), MAX_COMPONENT_NAME_LENGTH); | String projectName = abbreviate(defaultIfBlank(wsRequest.param(PARAM_PROJECT_NAME), projectKey), MAX_COMPONENT_NAME_LENGTH); | ||||
Map<String, String> characteristics = parseTaskCharacteristics(wsRequest); | Map<String, String> characteristics = parseTaskCharacteristics(wsRequest); | ||||
try (InputStream report = new BufferedInputStream(wsRequest.mandatoryParamAsPart(PARAM_REPORT_DATA).getInputStream())) { | try (InputStream report = new BufferedInputStream(wsRequest.mandatoryParamAsPart(PARAM_REPORT_DATA).getInputStream())) { | ||||
CeTask task = reportSubmitter.submit(organizationKey, projectKey, deprecatedBranch, projectName, characteristics, report); | |||||
CeTask task = reportSubmitter.submit(organizationKey, projectKey, projectName, characteristics, report); | |||||
Ce.SubmitResponse submitResponse = Ce.SubmitResponse.newBuilder() | Ce.SubmitResponse submitResponse = Ce.SubmitResponse.newBuilder() | ||||
.setTaskId(task.getUuid()) | .setTaskId(task.getUuid()) | ||||
.setProjectId(task.getComponent().get().getUuid()) | .setProjectId(task.getComponent().get().getUuid()) |
import org.sonar.api.resources.Qualifiers; | import org.sonar.api.resources.Qualifiers; | ||||
import org.sonar.api.resources.Scopes; | import org.sonar.api.resources.Scopes; | ||||
import org.sonar.api.utils.System2; | import org.sonar.api.utils.System2; | ||||
import org.sonar.core.component.ComponentKeys; | |||||
import org.sonar.core.i18n.I18n; | import org.sonar.core.i18n.I18n; | ||||
import org.sonar.core.util.Uuids; | import org.sonar.core.util.Uuids; | ||||
import org.sonar.db.DbClient; | import org.sonar.db.DbClient; | ||||
} | } | ||||
private ComponentDto createRootComponent(DbSession session, NewComponent newComponent) { | private ComponentDto createRootComponent(DbSession session, NewComponent newComponent) { | ||||
checkLegacyBranchFormat(newComponent.qualifier(), newComponent.deprecatedBranch()); | |||||
String keyWithBranch = ComponentKeys.createKey(newComponent.key(), newComponent.deprecatedBranch()); | |||||
checkRequest(!dbClient.componentDao().selectByKey(session, keyWithBranch).isPresent(), | |||||
"Could not create %s, key already exists: %s", getQualifierToDisplay(newComponent.qualifier()), keyWithBranch); | |||||
checkRequest(!dbClient.componentDao().selectByKey(session, newComponent.key()).isPresent(), | |||||
"Could not create %s, key already exists: %s", getQualifierToDisplay(newComponent.qualifier()), newComponent.key()); | |||||
String uuid = Uuids.create(); | String uuid = Uuids.create(); | ||||
ComponentDto component = new ComponentDto() | ComponentDto component = new ComponentDto() | ||||
.setModuleUuid(null) | .setModuleUuid(null) | ||||
.setModuleUuidPath(ComponentDto.UUID_PATH_SEPARATOR + uuid + ComponentDto.UUID_PATH_SEPARATOR) | .setModuleUuidPath(ComponentDto.UUID_PATH_SEPARATOR + uuid + ComponentDto.UUID_PATH_SEPARATOR) | ||||
.setProjectUuid(uuid) | .setProjectUuid(uuid) | ||||
.setDbKey(keyWithBranch) | |||||
.setDbKey(newComponent.key()) | |||||
.setName(newComponent.name()) | .setName(newComponent.name()) | ||||
.setLongName(newComponent.name()) | .setLongName(newComponent.name()) | ||||
.setScope(Scopes.PROJECT) | .setScope(Scopes.PROJECT) | ||||
checkRequest(isValidProjectKey(key), "Malformed key for %s: '%s'. It cannot be empty nor contain whitespaces.", getQualifierToDisplay(qualifier), key); | checkRequest(isValidProjectKey(key), "Malformed key for %s: '%s'. It cannot be empty nor contain whitespaces.", getQualifierToDisplay(qualifier), key); | ||||
} | } | ||||
private void checkLegacyBranchFormat(String qualifier, @Nullable String branch) { | |||||
checkRequest(branch == null || ComponentKeys.isValidLegacyBranch(branch), | |||||
"Malformed branch for %s: %s. Allowed characters are alphanumeric, '-', '_', '.' and '/', with at least one non-digit.", getQualifierToDisplay(qualifier), branch); | |||||
} | |||||
private String getQualifierToDisplay(String qualifier) { | private String getQualifierToDisplay(String qualifier) { | ||||
return i18n.message(Locale.getDefault(), "qualifier." + qualifier, "Project"); | return i18n.message(Locale.getDefault(), "qualifier." + qualifier, "Project"); | ||||
} | } |
*/ | */ | ||||
package org.sonar.server.component; | package org.sonar.server.component; | ||||
import javax.annotation.CheckForNull; | |||||
import javax.annotation.Nullable; | |||||
import javax.annotation.concurrent.Immutable; | import javax.annotation.concurrent.Immutable; | ||||
import static java.util.Objects.requireNonNull; | import static java.util.Objects.requireNonNull; | ||||
public class NewComponent { | public class NewComponent { | ||||
private final String organizationUuid; | private final String organizationUuid; | ||||
private final String key; | private final String key; | ||||
private final String branch; | |||||
private final String qualifier; | private final String qualifier; | ||||
private final String name; | private final String name; | ||||
private final boolean isPrivate; | private final boolean isPrivate; | ||||
private NewComponent(NewComponent.Builder builder) { | private NewComponent(NewComponent.Builder builder) { | ||||
this.organizationUuid = builder.organizationUuid; | this.organizationUuid = builder.organizationUuid; | ||||
this.key = builder.key; | this.key = builder.key; | ||||
this.branch = builder.branch; | |||||
this.qualifier = builder.qualifier; | this.qualifier = builder.qualifier; | ||||
this.name = builder.name; | this.name = builder.name; | ||||
this.isPrivate = builder.isPrivate; | this.isPrivate = builder.isPrivate; | ||||
return name; | return name; | ||||
} | } | ||||
@CheckForNull | |||||
public String deprecatedBranch() { | |||||
return branch; | |||||
} | |||||
public String qualifier() { | public String qualifier() { | ||||
return qualifier; | return qualifier; | ||||
} | } | ||||
private String organizationUuid; | private String organizationUuid; | ||||
private String key; | private String key; | ||||
private String qualifier = PROJECT; | private String qualifier = PROJECT; | ||||
private String branch; | |||||
private String name; | private String name; | ||||
private boolean isPrivate = false; | private boolean isPrivate = false; | ||||
return this; | return this; | ||||
} | } | ||||
public Builder setDeprecatedBranch(@Nullable String s) { | |||||
this.branch = s; | |||||
return this; | |||||
} | |||||
public Builder setQualifier(String qualifier) { | public Builder setQualifier(String qualifier) { | ||||
this.qualifier = qualifier; | this.qualifier = qualifier; | ||||
return this; | return this; |
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | ||||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | import static org.sonar.server.ws.WsUtils.writeProtobuf; | ||||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_CREATE; | import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_CREATE; | ||||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_BRANCH; | |||||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NAME; | import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NAME; | ||||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT; | import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT; | ||||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_VISIBILITY; | import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_VISIBILITY; | ||||
.setRequired(true) | .setRequired(true) | ||||
.setExampleValue("SonarQube"); | .setExampleValue("SonarQube"); | ||||
action.createParam(PARAM_BRANCH) | |||||
.setDeprecatedSince("7.8") | |||||
.setDescription("SCM Branch of the project. The key of the project will become key:branch, for instance 'SonarQube:branch-5.0'") | |||||
.setExampleValue("branch-5.0"); | |||||
action.createParam(PARAM_VISIBILITY) | action.createParam(PARAM_VISIBILITY) | ||||
.setDescription("Whether the created project should be visible to everyone, or only specific user/groups.<br/>" + | .setDescription("Whether the created project should be visible to everyone, or only specific user/groups.<br/>" + | ||||
"If no visibility is specified, the default project visibility of the organization will be used.") | "If no visibility is specified, the default project visibility of the organization will be used.") | ||||
.setOrganizationUuid(organization.getUuid()) | .setOrganizationUuid(organization.getUuid()) | ||||
.setKey(request.getProjectKey()) | .setKey(request.getProjectKey()) | ||||
.setName(request.getName()) | .setName(request.getName()) | ||||
.setDeprecatedBranch(request.getBranch()) | |||||
.setPrivate(changeToPrivate) | .setPrivate(changeToPrivate) | ||||
.setQualifier(PROJECT) | .setQualifier(PROJECT) | ||||
.build(), | .build(), | ||||
.setOrganization(request.param(PARAM_ORGANIZATION)) | .setOrganization(request.param(PARAM_ORGANIZATION)) | ||||
.setProjectKey(request.mandatoryParam(PARAM_PROJECT)) | .setProjectKey(request.mandatoryParam(PARAM_PROJECT)) | ||||
.setName(abbreviate(request.mandatoryParam(PARAM_NAME), MAX_COMPONENT_NAME_LENGTH)) | .setName(abbreviate(request.mandatoryParam(PARAM_NAME), MAX_COMPONENT_NAME_LENGTH)) | ||||
.setBranch(request.param(PARAM_BRANCH)) | |||||
.setVisibility(request.param(PARAM_VISIBILITY)) | .setVisibility(request.param(PARAM_VISIBILITY)) | ||||
.build(); | .build(); | ||||
} | } | ||||
private final String organization; | private final String organization; | ||||
private final String projectKey; | private final String projectKey; | ||||
private final String name; | private final String name; | ||||
private final String branch; | |||||
@CheckForNull | @CheckForNull | ||||
private final String visibility; | private final String visibility; | ||||
this.organization = builder.organization; | this.organization = builder.organization; | ||||
this.projectKey = builder.projectKey; | this.projectKey = builder.projectKey; | ||||
this.name = builder.name; | this.name = builder.name; | ||||
this.branch = builder.branch; | |||||
this.visibility = builder.visibility; | this.visibility = builder.visibility; | ||||
} | } | ||||
return name; | return name; | ||||
} | } | ||||
@CheckForNull | |||||
public String getBranch() { | |||||
return branch; | |||||
} | |||||
@CheckForNull | @CheckForNull | ||||
public String getVisibility() { | public String getVisibility() { | ||||
return visibility; | return visibility; | ||||
private String organization; | private String organization; | ||||
private String projectKey; | private String projectKey; | ||||
private String name; | private String name; | ||||
private String branch; | |||||
@CheckForNull | @CheckForNull | ||||
private String visibility; | private String visibility; | ||||
return this; | return this; | ||||
} | } | ||||
public Builder setBranch(@Nullable String branch) { | |||||
this.branch = branch; | |||||
return this; | |||||
} | |||||
public Builder setVisibility(@Nullable String visibility) { | public Builder setVisibility(@Nullable String visibility) { | ||||
this.visibility = visibility; | this.visibility = visibility; | ||||
return this; | return this; |
import org.sonar.ce.queue.CeQueue; | import org.sonar.ce.queue.CeQueue; | ||||
import org.sonar.ce.queue.CeQueueImpl; | import org.sonar.ce.queue.CeQueueImpl; | ||||
import org.sonar.ce.queue.CeTaskSubmit; | import org.sonar.ce.queue.CeTaskSubmit; | ||||
import org.sonar.core.component.ComponentKeys; | |||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.DbTester; | import org.sonar.db.DbTester; | ||||
import org.sonar.db.ce.CeTaskTypes; | import org.sonar.db.ce.CeTaskTypes; | ||||
mockSuccessfulPrepareSubmitCall(); | mockSuccessfulPrepareSubmitCall(); | ||||
InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8); | InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8); | ||||
underTest.submit(organization.getKey(), project.getDbKey(), null, project.name(), emptyMap(), reportInput); | |||||
underTest.submit(organization.getKey(), project.getDbKey(), project.name(), emptyMap(), reportInput); | |||||
verifyZeroInteractions(branchSupportDelegate); | verifyZeroInteractions(branchSupportDelegate); | ||||
} | } | ||||
InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8); | InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8); | ||||
String taskUuid = mockSuccessfulPrepareSubmitCall(); | String taskUuid = mockSuccessfulPrepareSubmitCall(); | ||||
underTest.submit(organization.getKey(), project.getDbKey(), null, project.name(), randomCharacteristics, reportInput); | |||||
underTest.submit(organization.getKey(), project.getDbKey(), project.name(), randomCharacteristics, reportInput); | |||||
verifyZeroInteractions(permissionTemplateService); | verifyZeroInteractions(permissionTemplateService); | ||||
verifyZeroInteractions(favoriteUpdater); | verifyZeroInteractions(favoriteUpdater); | ||||
verifyQueueSubmit(project, branch, user, randomCharacteristics, taskUuid); | verifyQueueSubmit(project, branch, user, randomCharacteristics, taskUuid); | ||||
} | } | ||||
@Test | |||||
public void submit_a_report_on_existing_deprecated_branch() { | |||||
OrganizationDto organization = db.organizations().insert(); | |||||
String projectKey = randomAlphabetic(10); | |||||
String deprecatedBranchName = randomAlphabetic(11); | |||||
ComponentDto deprecatedBranch = db.components().insertMainBranch(organization, cpt -> cpt.setDbKey(ComponentKeys.createKey(projectKey, deprecatedBranchName))); | |||||
UserDto user = db.users().insertUser(); | |||||
userSession.logIn(user).addProjectPermission(SCAN_EXECUTION, deprecatedBranch); | |||||
Map<String, String> noCharacteristics = emptyMap(); | |||||
InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8); | |||||
String taskUuid = mockSuccessfulPrepareSubmitCall(); | |||||
underTest.submit(organization.getKey(), deprecatedBranch.getDbKey(), null, deprecatedBranch.name(), noCharacteristics, reportInput); | |||||
verifyZeroInteractions(permissionTemplateService); | |||||
verifyZeroInteractions(favoriteUpdater); | |||||
verify(branchSupport, times(0)).createBranchComponent(any(), any(), any(), any(), any()); | |||||
verifyZeroInteractions(branchSupportDelegate); | |||||
verifyQueueSubmit(deprecatedBranch, deprecatedBranch, user, noCharacteristics, taskUuid); | |||||
} | |||||
@Test | @Test | ||||
public void submit_a_report_on_missing_branch_but_existing_project() { | public void submit_a_report_on_missing_branch_but_existing_project() { | ||||
OrganizationDto organization = db.organizations().insert(); | OrganizationDto organization = db.organizations().insert(); | ||||
InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8); | InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8); | ||||
String taskUuid = mockSuccessfulPrepareSubmitCall(); | String taskUuid = mockSuccessfulPrepareSubmitCall(); | ||||
underTest.submit(organization.getKey(), existingProject.getDbKey(), null, existingProject.name(), randomCharacteristics, reportInput); | |||||
underTest.submit(organization.getKey(), existingProject.getDbKey(), existingProject.name(), randomCharacteristics, reportInput); | |||||
verifyZeroInteractions(permissionTemplateService); | verifyZeroInteractions(permissionTemplateService); | ||||
verifyZeroInteractions(favoriteUpdater); | verifyZeroInteractions(favoriteUpdater); | ||||
String taskUuid = mockSuccessfulPrepareSubmitCall(); | String taskUuid = mockSuccessfulPrepareSubmitCall(); | ||||
InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8); | InputStream reportInput = IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8); | ||||
underTest.submit(organization.getKey(), nonExistingProject.getDbKey(), null, nonExistingProject.name(), randomCharacteristics, reportInput); | |||||
underTest.submit(organization.getKey(), nonExistingProject.getDbKey(), nonExistingProject.name(), randomCharacteristics, reportInput); | |||||
BranchDto exitingProjectMainBranch = db.getDbClient().branchDao().selectByUuid(db.getSession(), nonExistingProject.uuid()).get(); | BranchDto exitingProjectMainBranch = db.getDbClient().branchDao().selectByUuid(db.getSession(), nonExistingProject.uuid()).get(); | ||||
verify(branchSupport).createBranchComponent(any(DbSession.class), same(componentKey), eq(organization), eq(nonExistingProject), eq(exitingProjectMainBranch)); | verify(branchSupport).createBranchComponent(any(DbSession.class), same(componentKey), eq(organization), eq(nonExistingProject), eq(exitingProjectMainBranch)); | ||||
when(branchSupportDelegate.createComponentKey(any(), any())).thenThrow(expected); | when(branchSupportDelegate.createComponentKey(any(), any())).thenThrow(expected); | ||||
try { | try { | ||||
underTest.submit(organization.getKey(), project.getDbKey(), null, project.name(), randomCharacteristics, reportInput); | |||||
underTest.submit(organization.getKey(), project.getDbKey(), project.name(), randomCharacteristics, reportInput); | |||||
fail("exception should have been thrown"); | fail("exception should have been thrown"); | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
assertThat(e).isSameAs(expected); | assertThat(e).isSameAs(expected); | ||||
expectedException.expect(ForbiddenException.class); | expectedException.expect(ForbiddenException.class); | ||||
expectedException.expectMessage("Insufficient privileges"); | expectedException.expectMessage("Insufficient privileges"); | ||||
underTest.submit(organization.getKey(), nonExistingProject.getDbKey(), null, nonExistingProject.name(), randomCharacteristics, reportInput); | |||||
underTest.submit(organization.getKey(), nonExistingProject.getDbKey(), nonExistingProject.name(), randomCharacteristics, reportInput); | |||||
} | } | ||||
private static ComponentDto createButDoNotInsertBranch(ComponentDto project) { | private static ComponentDto createButDoNotInsertBranch(ComponentDto project) { |
import com.tngtech.java.junit.dataprovider.DataProvider; | import com.tngtech.java.junit.dataprovider.DataProvider; | ||||
import com.tngtech.java.junit.dataprovider.DataProviderRunner; | import com.tngtech.java.junit.dataprovider.DataProviderRunner; | ||||
import com.tngtech.java.junit.dataprovider.UseDataProvider; | |||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Random; | import java.util.Random; | ||||
public void createComponentKey_of_main_branch() { | public void createComponentKey_of_main_branch() { | ||||
String projectKey = randomAlphanumeric(12); | String projectKey = randomAlphanumeric(12); | ||||
ComponentKey componentKey = underTestNoBranch.createComponentKey(projectKey, null, NO_CHARACTERISTICS); | |||||
ComponentKey componentKey = underTestNoBranch.createComponentKey(projectKey, NO_CHARACTERISTICS); | |||||
assertThat(componentKey) | assertThat(componentKey) | ||||
.isEqualTo(underTestWithBranch.createComponentKey(projectKey, null, NO_CHARACTERISTICS)); | |||||
.isEqualTo(underTestWithBranch.createComponentKey(projectKey, NO_CHARACTERISTICS)); | |||||
assertThat(componentKey.getKey()).isEqualTo(projectKey); | assertThat(componentKey.getKey()).isEqualTo(projectKey); | ||||
assertThat(componentKey.getDbKey()).isEqualTo(projectKey); | assertThat(componentKey.getDbKey()).isEqualTo(projectKey); | ||||
assertThat(componentKey.isDeprecatedBranch()).isFalse(); | |||||
assertThat(componentKey.getMainBranchComponentKey()).isSameAs(componentKey); | assertThat(componentKey.getMainBranchComponentKey()).isSameAs(componentKey); | ||||
assertThat(componentKey.getBranch()).isEmpty(); | assertThat(componentKey.getBranch()).isEmpty(); | ||||
assertThat(componentKey.getPullRequestKey()).isEmpty(); | assertThat(componentKey.getPullRequestKey()).isEmpty(); | ||||
} | } | ||||
@Test | |||||
public void createComponentKey_of_deprecated_branch() { | |||||
String projectKey = randomAlphanumeric(12); | |||||
String deprecatedBranchName = randomAlphanumeric(12); | |||||
ComponentKey componentKey = underTestNoBranch.createComponentKey(projectKey, deprecatedBranchName, NO_CHARACTERISTICS); | |||||
assertThat(componentKey) | |||||
.isEqualTo(underTestWithBranch.createComponentKey(projectKey, deprecatedBranchName, NO_CHARACTERISTICS)); | |||||
assertThat(componentKey.getKey()).isEqualTo(projectKey); | |||||
assertThat(componentKey.getDbKey()).isEqualTo(projectKey + ":" + deprecatedBranchName); | |||||
assertThat(componentKey.isDeprecatedBranch()).isTrue(); | |||||
assertThat(componentKey.getMainBranchComponentKey()).isSameAs(componentKey); | |||||
assertThat(componentKey.getBranch()).isEmpty(); | |||||
assertThat(componentKey.getPullRequestKey()).isEmpty(); | |||||
} | |||||
@Test | |||||
@UseDataProvider("nullOrNonEmpty") | |||||
public void createComponentKey_with_ISE_if_characteristics_is_not_empty_and_delegate_is_null(String deprecatedBranchName) { | |||||
String projectKey = randomAlphanumeric(12); | |||||
Map<String, String> nonEmptyMap = newRandomNonEmptyMap(); | |||||
expectedException.expect(IllegalStateException.class); | |||||
expectedException.expectMessage("Current edition does not support branch feature"); | |||||
underTestNoBranch.createComponentKey(projectKey, deprecatedBranchName, nonEmptyMap); | |||||
} | |||||
@Test | |||||
public void createComponentKey_fails_with_IAE_if_characteristics_is_not_empty_and_deprecatedBranchName_is_non_null() { | |||||
String projectKey = randomAlphanumeric(12); | |||||
String deprecatedBranchName = randomAlphanumeric(13); | |||||
Map<String, String> nonEmptyMap = newRandomNonEmptyMap(); | |||||
expectedException.expect(IllegalArgumentException.class); | |||||
expectedException.expectMessage("Deprecated branch feature can't be used at the same time as new branch support"); | |||||
underTestWithBranch.createComponentKey(projectKey, deprecatedBranchName, nonEmptyMap); | |||||
} | |||||
@Test | @Test | ||||
public void createComponentKey_delegates_to_delegate_if_characteristics_is_not_empty() { | public void createComponentKey_delegates_to_delegate_if_characteristics_is_not_empty() { | ||||
String projectKey = randomAlphanumeric(12); | String projectKey = randomAlphanumeric(12); | ||||
ComponentKey expected = mock(ComponentKey.class); | ComponentKey expected = mock(ComponentKey.class); | ||||
when(branchSupportDelegate.createComponentKey(projectKey, nonEmptyMap)).thenReturn(expected); | when(branchSupportDelegate.createComponentKey(projectKey, nonEmptyMap)).thenReturn(expected); | ||||
ComponentKey componentKey = underTestWithBranch.createComponentKey(projectKey, null, nonEmptyMap); | |||||
ComponentKey componentKey = underTestWithBranch.createComponentKey(projectKey, nonEmptyMap); | |||||
assertThat(componentKey).isSameAs(expected); | assertThat(componentKey).isSameAs(expected); | ||||
} | } | ||||
expectedException.expect(IllegalStateException.class); | expectedException.expect(IllegalStateException.class); | ||||
expectedException.expectMessage("Current edition does not support branch feature"); | expectedException.expectMessage("Current edition does not support branch feature"); | ||||
underTestNoBranch.createBranchComponent(dbSession, componentKey, organization, mainComponentDto, mainComponentBranchDto); | underTestNoBranch.createBranchComponent(dbSession, componentKey, organization, mainComponentDto, mainComponentBranchDto); | ||||
} | } | ||||
import org.sonar.ce.queue.CeQueueImpl; | import org.sonar.ce.queue.CeQueueImpl; | ||||
import org.sonar.ce.queue.CeTaskSubmit; | import org.sonar.ce.queue.CeTaskSubmit; | ||||
import org.sonar.core.i18n.I18n; | import org.sonar.core.i18n.I18n; | ||||
import org.sonar.core.permission.GlobalPermissions; | |||||
import org.sonar.db.DbSession; | import org.sonar.db.DbSession; | ||||
import org.sonar.db.DbTester; | import org.sonar.db.DbTester; | ||||
import org.sonar.db.ce.CeTaskTypes; | import org.sonar.db.ce.CeTaskTypes; | ||||
expectedException.expect(IllegalStateException.class); | expectedException.expect(IllegalStateException.class); | ||||
expectedException.expectMessage("Current edition does not support branch feature"); | expectedException.expectMessage("Current edition does not support branch feature"); | ||||
underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, nonEmptyCharacteristics, reportInput); | |||||
underTest.submit(defaultOrganizationKey, PROJECT_KEY, PROJECT_NAME, nonEmptyCharacteristics, reportInput); | |||||
} | } | ||||
@Test | @Test | ||||
when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(), eq(defaultOrganizationUuid), any(), eq(PROJECT_KEY))) | when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(), eq(defaultOrganizationUuid), any(), eq(PROJECT_KEY))) | ||||
.thenReturn(true); | .thenReturn(true); | ||||
underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}", UTF_8)); | |||||
underTest.submit(defaultOrganizationKey, PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}", UTF_8)); | |||||
verifyReportIsPersisted(TASK_UUID); | verifyReportIsPersisted(TASK_UUID); | ||||
} | } | ||||
userSession.logIn(user).addProjectPermission(SCAN_EXECUTION, project); | userSession.logIn(user).addProjectPermission(SCAN_EXECUTION, project); | ||||
mockSuccessfulPrepareSubmitCall(); | mockSuccessfulPrepareSubmitCall(); | ||||
underTest.submit(defaultOrganizationKey, project.getDbKey(), null, project.name(), emptyMap(), IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8)); | |||||
underTest.submit(defaultOrganizationKey, project.getDbKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8)); | |||||
verifyReportIsPersisted(TASK_UUID); | verifyReportIsPersisted(TASK_UUID); | ||||
verifyZeroInteractions(permissionTemplateService); | verifyZeroInteractions(permissionTemplateService); | ||||
when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(organization.getUuid()), any(), eq(PROJECT_KEY))).thenReturn(true); | when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(organization.getUuid()), any(), eq(PROJECT_KEY))).thenReturn(true); | ||||
when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(true); | when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(true); | ||||
underTest.submit(organization.getKey(), PROJECT_KEY, null, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
underTest.submit(organization.getKey(), PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get(); | ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get(); | ||||
verifyReportIsPersisted(TASK_UUID); | verifyReportIsPersisted(TASK_UUID); | ||||
when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(organization.getUuid()), any(), eq(PROJECT_KEY))).thenReturn(true); | when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(organization.getUuid()), any(), eq(PROJECT_KEY))).thenReturn(true); | ||||
when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(true); | when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(true); | ||||
underTest.submit(organization.getKey(), PROJECT_KEY, null, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
underTest.submit(organization.getKey(), PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get(); | ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get(); | ||||
assertThat(db.favorites().hasFavorite(createdProject, user.getId())).isTrue(); | assertThat(db.favorites().hasFavorite(createdProject, user.getId())).isTrue(); | ||||
when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(false); | when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(false); | ||||
mockSuccessfulPrepareSubmitCall(); | mockSuccessfulPrepareSubmitCall(); | ||||
underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
underTest.submit(defaultOrganizationKey, PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get(); | ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get(); | ||||
assertThat(db.favorites().hasNoFavorite(createdProject)).isTrue(); | assertThat(db.favorites().hasNoFavorite(createdProject)).isTrue(); | ||||
when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(organization.getUuid()), any(), eq(PROJECT_KEY))).thenReturn(true); | when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(organization.getUuid()), any(), eq(PROJECT_KEY))).thenReturn(true); | ||||
when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(true); | when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(true); | ||||
underTest.submit(organization.getKey(), PROJECT_KEY, null, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
underTest.submit(organization.getKey(), PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get(); | ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get(); | ||||
assertThat(db.favorites().hasNoFavorite(createdProject)).isTrue(); | assertThat(db.favorites().hasNoFavorite(createdProject)).isTrue(); | ||||
when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(defaultOrganizationUuid), any(), eq(PROJECT_KEY))) | when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(defaultOrganizationUuid), any(), eq(PROJECT_KEY))) | ||||
.thenReturn(true); | .thenReturn(true); | ||||
underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
underTest.submit(defaultOrganizationKey, PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
verify(queue).submit(any(CeTaskSubmit.class)); | verify(queue).submit(any(CeTaskSubmit.class)); | ||||
} | } | ||||
userSession.addPermission(SCAN, org); | userSession.addPermission(SCAN, org); | ||||
mockSuccessfulPrepareSubmitCall(); | mockSuccessfulPrepareSubmitCall(); | ||||
underTest.submit(org.getKey(), project.getDbKey(), null, project.name(), emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
underTest.submit(org.getKey(), project.getDbKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
verify(queue).submit(any(CeTaskSubmit.class)); | verify(queue).submit(any(CeTaskSubmit.class)); | ||||
} | } | ||||
userSession.addProjectPermission(SCAN_EXECUTION, project); | userSession.addProjectPermission(SCAN_EXECUTION, project); | ||||
mockSuccessfulPrepareSubmitCall(); | mockSuccessfulPrepareSubmitCall(); | ||||
underTest.submit(defaultOrganizationKey, project.getDbKey(), null, project.name(), emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
underTest.submit(defaultOrganizationKey, project.getDbKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
verify(queue).submit(any(CeTaskSubmit.class)); | verify(queue).submit(any(CeTaskSubmit.class)); | ||||
} | } | ||||
/** | |||||
* SONAR-8757 | |||||
*/ | |||||
@Test | |||||
public void project_branch_must_not_benefit_from_the_scan_permission_on_main_project() { | |||||
String branchName = "branchFoo"; | |||||
ComponentDto mainProject = db.components().insertPrivateProject(); | |||||
userSession.addProjectPermission(GlobalPermissions.SCAN_EXECUTION, mainProject); | |||||
// user does not have the "scan" permission on the branch, so it can't scan it | |||||
ComponentDto branchProject = db.components().insertPrivateProject(p -> p.setDbKey(mainProject.getDbKey() + ":" + branchName)); | |||||
expectedException.expect(ForbiddenException.class); | |||||
underTest.submit(defaultOrganizationKey, mainProject.getDbKey(), branchName, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
} | |||||
@Test | @Test | ||||
public void fail_with_NotFoundException_if_organization_with_specified_key_does_not_exist() { | public void fail_with_NotFoundException_if_organization_with_specified_key_does_not_exist() { | ||||
expectedException.expect(NotFoundException.class); | expectedException.expect(NotFoundException.class); | ||||
expectedException.expectMessage("Organization with key 'fop' does not exist"); | expectedException.expectMessage("Organization with key 'fop' does not exist"); | ||||
underTest.submit("fop", PROJECT_KEY, null, null, emptyMap(), null /* method will fail before parameter is used */); | |||||
underTest.submit("fop", PROJECT_KEY, null, emptyMap(), null /* method will fail before parameter is used */); | |||||
} | } | ||||
@Test | @Test | ||||
ComponentDto project = db.components().insertPrivateProject(organization); | ComponentDto project = db.components().insertPrivateProject(organization); | ||||
mockSuccessfulPrepareSubmitCall(); | mockSuccessfulPrepareSubmitCall(); | ||||
underTest.submit(organization.getKey(), project.getDbKey(), null, project.name(), emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
underTest.submit(organization.getKey(), project.getDbKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
} | } | ||||
@Test | @Test | ||||
expectedException.expect(BadRequestException.class); | expectedException.expect(BadRequestException.class); | ||||
expectedException.expectMessage(format("Component '%s' is not a project", component.getKey())); | expectedException.expectMessage(format("Component '%s' is not a project", component.getKey())); | ||||
underTest.submit(defaultOrganizationKey, component.getDbKey(), null, component.name(), emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
underTest.submit(defaultOrganizationKey, component.getDbKey(), component.name(), emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
} | } | ||||
@Test | @Test | ||||
mockSuccessfulPrepareSubmitCall(); | mockSuccessfulPrepareSubmitCall(); | ||||
try { | try { | ||||
underTest.submit(defaultOrganizationKey, module.getDbKey(), null, module.name(), emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
underTest.submit(defaultOrganizationKey, module.getDbKey(), module.name(), emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
fail(); | fail(); | ||||
} catch (BadRequestException e) { | } catch (BadRequestException e) { | ||||
assertThat(e.errors()).contains( | assertThat(e.errors()).contains( | ||||
public void fail_with_forbidden_exception_when_no_scan_permission() { | public void fail_with_forbidden_exception_when_no_scan_permission() { | ||||
expectedException.expect(ForbiddenException.class); | expectedException.expect(ForbiddenException.class); | ||||
underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
underTest.submit(defaultOrganizationKey, PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
} | } | ||||
@Test | @Test | ||||
mockSuccessfulPrepareSubmitCall(); | mockSuccessfulPrepareSubmitCall(); | ||||
expectedException.expect(ForbiddenException.class); | expectedException.expect(ForbiddenException.class); | ||||
underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
underTest.submit(defaultOrganizationKey, PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}")); | |||||
} | } | ||||
private void verifyReportIsPersisted(String taskUuid) { | private void verifyReportIsPersisted(String taskUuid) { |
import static org.mockito.ArgumentMatchers.any; | import static org.mockito.ArgumentMatchers.any; | ||||
import static org.mockito.ArgumentMatchers.anyMap; | import static org.mockito.ArgumentMatchers.anyMap; | ||||
import static org.mockito.ArgumentMatchers.eq; | import static org.mockito.ArgumentMatchers.eq; | ||||
import static org.mockito.ArgumentMatchers.isNull; | |||||
import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||
import static org.mockito.Mockito.verify; | import static org.mockito.Mockito.verify; | ||||
import static org.mockito.Mockito.when; | import static org.mockito.Mockito.when; | ||||
@Test | @Test | ||||
public void submit_task_to_the_queue_and_ask_for_immediate_processing() { | public void submit_task_to_the_queue_and_ask_for_immediate_processing() { | ||||
when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), isNull(), eq("My Project"), | |||||
anyMap(), any())).thenReturn(A_CE_TASK); | |||||
when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), eq("My Project"), anyMap(), any())).thenReturn(A_CE_TASK); | |||||
Ce.SubmitResponse submitResponse = tester.newRequest() | Ce.SubmitResponse submitResponse = tester.newRequest() | ||||
.setParam("projectKey", "my_project") | .setParam("projectKey", "my_project") | ||||
.setMethod("POST") | .setMethod("POST") | ||||
.executeProtobuf(Ce.SubmitResponse.class); | .executeProtobuf(Ce.SubmitResponse.class); | ||||
verify(reportSubmitter).submit(eq(organizationKey), eq("my_project"), isNull(), eq("My Project"), | |||||
anyMap(), any()); | |||||
verify(reportSubmitter).submit(eq(organizationKey), eq("my_project"), eq("My Project"), anyMap(), any()); | |||||
assertThat(submitResponse.getTaskId()).isEqualTo("TASK_1"); | assertThat(submitResponse.getTaskId()).isEqualTo("TASK_1"); | ||||
assertThat(submitResponse.getProjectId()).isEqualTo(PROJECT_UUID); | assertThat(submitResponse.getProjectId()).isEqualTo(PROJECT_UUID); | ||||
@Test | @Test | ||||
public void submit_task_with_characteristics() { | public void submit_task_with_characteristics() { | ||||
when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), isNull(), eq("My Project"), | |||||
anyMap(), any())).thenReturn(A_CE_TASK); | |||||
when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), eq("My Project"), anyMap(), any())).thenReturn(A_CE_TASK); | |||||
String[] characteristics = {"branch=foo", "pullRequest=123", "unsupported=bar"}; | String[] characteristics = {"branch=foo", "pullRequest=123", "unsupported=bar"}; | ||||
Ce.SubmitResponse submitResponse = tester.newRequest() | Ce.SubmitResponse submitResponse = tester.newRequest() | ||||
.executeProtobuf(Ce.SubmitResponse.class); | .executeProtobuf(Ce.SubmitResponse.class); | ||||
assertThat(submitResponse.getTaskId()).isEqualTo("TASK_1"); | assertThat(submitResponse.getTaskId()).isEqualTo("TASK_1"); | ||||
verify(reportSubmitter).submit(eq(organizationKey), eq("my_project"), isNull(), eq("My Project"), | |||||
map.capture(), any()); | |||||
verify(reportSubmitter).submit(eq(organizationKey), eq("my_project"), eq("My Project"), map.capture(), any()); | |||||
// unsupported characteristics are ignored | // unsupported characteristics are ignored | ||||
assertThat(map.getValue()).containsExactly(entry("branch", "foo"), entry("pullRequest", "123")); | assertThat(map.getValue()).containsExactly(entry("branch", "foo"), entry("pullRequest", "123")); | ||||
public void abbreviate_long_name() { | public void abbreviate_long_name() { | ||||
String longName = Strings.repeat("a", 1_000); | String longName = Strings.repeat("a", 1_000); | ||||
String expectedName = Strings.repeat("a", 497) + "..."; | String expectedName = Strings.repeat("a", 497) + "..."; | ||||
when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), isNull(), eq(expectedName), | |||||
anyMap(), any())).thenReturn(A_CE_TASK); | |||||
when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), eq(expectedName), anyMap(), any())).thenReturn(A_CE_TASK); | |||||
Ce.SubmitResponse submitResponse = tester.newRequest() | Ce.SubmitResponse submitResponse = tester.newRequest() | ||||
.setParam("projectKey", "my_project") | .setParam("projectKey", "my_project") | ||||
.setMethod("POST") | .setMethod("POST") | ||||
.executeProtobuf(Ce.SubmitResponse.class); | .executeProtobuf(Ce.SubmitResponse.class); | ||||
verify(reportSubmitter).submit(eq(organizationKey), eq("my_project"), isNull(), eq(expectedName), | |||||
anyMap(), any()); | |||||
verify(reportSubmitter).submit(eq(organizationKey), eq("my_project"), eq(expectedName), anyMap(), any()); | |||||
assertThat(submitResponse.getTaskId()).isEqualTo("TASK_1"); | assertThat(submitResponse.getTaskId()).isEqualTo("TASK_1"); | ||||
assertThat(submitResponse.getProjectId()).isEqualTo(PROJECT_UUID); | assertThat(submitResponse.getProjectId()).isEqualTo(PROJECT_UUID); | ||||
@Test | @Test | ||||
public void test_example_json_response() { | public void test_example_json_response() { | ||||
when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), isNull(), eq("My Project"), | |||||
anyMap(), any())).thenReturn(A_CE_TASK); | |||||
when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), eq("My Project"), anyMap(), any())).thenReturn(A_CE_TASK); | |||||
TestResponse wsResponse = tester.newRequest() | TestResponse wsResponse = tester.newRequest() | ||||
.setParam("projectKey", "my_project") | .setParam("projectKey", "my_project") | ||||
*/ | */ | ||||
@Test | @Test | ||||
public void project_name_is_optional() { | public void project_name_is_optional() { | ||||
when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), isNull(), eq("my_project"), | |||||
anyMap(), any())).thenReturn(A_CE_TASK); | |||||
when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), eq("my_project"), anyMap(), any())).thenReturn(A_CE_TASK); | |||||
tester.newRequest() | tester.newRequest() | ||||
.setParam("projectKey", "my_project") | .setParam("projectKey", "my_project") | ||||
.setMethod("POST") | .setMethod("POST") | ||||
.execute(); | .execute(); | ||||
verify(reportSubmitter).submit(eq(organizationKey), eq("my_project"), isNull(), eq("my_project"), | |||||
anyMap(), any()); | |||||
verify(reportSubmitter).submit(eq(organizationKey), eq("my_project"), eq("my_project"), anyMap(), any()); | |||||
} | } | ||||
} | } |
assertThat(loaded.isPrivate()).isEqualTo(project.isPrivate()); | assertThat(loaded.isPrivate()).isEqualTo(project.isPrivate()); | ||||
} | } | ||||
@Test | |||||
public void create_project_with_deprecated_branch() { | |||||
ComponentDto project = underTest.create(db.getSession(), | |||||
NewComponent.newComponentBuilder() | |||||
.setKey(DEFAULT_PROJECT_KEY) | |||||
.setName(DEFAULT_PROJECT_NAME) | |||||
.setDeprecatedBranch("origin/master") | |||||
.setOrganizationUuid(db.getDefaultOrganization().getUuid()) | |||||
.build(), | |||||
null); | |||||
assertThat(project.getDbKey()).isEqualTo("project-key:origin/master"); | |||||
} | |||||
@Test | @Test | ||||
public void create_view() { | public void create_view() { | ||||
NewComponent view = NewComponent.newComponentBuilder() | NewComponent view = NewComponent.newComponentBuilder() | ||||
.build(), | .build(), | ||||
null); | null); | ||||
} | } | ||||
@Test | |||||
public void fail_to_create_new_component_on_invalid_branch() { | |||||
expectedException.expect(BadRequestException.class); | |||||
expectedException.expectMessage("Malformed branch for Project: origin?branch. Allowed characters are alphanumeric, '-', '_', '.' and '/', with at least one non-digit."); | |||||
underTest.create(db.getSession(), | |||||
NewComponent.newComponentBuilder() | |||||
.setKey(DEFAULT_PROJECT_KEY) | |||||
.setName(DEFAULT_PROJECT_NAME) | |||||
.setDeprecatedBranch("origin?branch") | |||||
.setOrganizationUuid(db.getDefaultOrganization().getUuid()) | |||||
.build(), | |||||
null); | |||||
} | |||||
} | } |
import static org.sonar.server.project.ws.ProjectsWsSupport.PARAM_ORGANIZATION; | import static org.sonar.server.project.ws.ProjectsWsSupport.PARAM_ORGANIZATION; | ||||
import static org.sonar.test.JsonAssert.assertJson; | import static org.sonar.test.JsonAssert.assertJson; | ||||
import static org.sonarqube.ws.client.WsRequest.Method.POST; | import static org.sonarqube.ws.client.WsRequest.Method.POST; | ||||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_BRANCH; | |||||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NAME; | import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NAME; | ||||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT; | import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT; | ||||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_VISIBILITY; | import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_VISIBILITY; | ||||
.containsOnly(DEFAULT_PROJECT_KEY, DEFAULT_PROJECT_NAME, "TRK", "PRJ", false, null); | .containsOnly(DEFAULT_PROJECT_KEY, DEFAULT_PROJECT_NAME, "TRK", "PRJ", false, null); | ||||
} | } | ||||
@Test | |||||
public void create_project_with_branch() { | |||||
userSession.addPermission(PROVISION_PROJECTS, db.getDefaultOrganization()); | |||||
CreateWsResponse response = call(CreateRequest.builder() | |||||
.setProjectKey(DEFAULT_PROJECT_KEY) | |||||
.setName(DEFAULT_PROJECT_NAME) | |||||
.setBranch("origin/master") | |||||
.build()); | |||||
assertThat(response.getProject()) | |||||
.extracting(Project::getKey, Project::getName, Project::getQualifier, Project::getVisibility) | |||||
.containsOnly(DEFAULT_PROJECT_KEY + ":origin/master", DEFAULT_PROJECT_NAME, "TRK", "public"); | |||||
} | |||||
@Test | @Test | ||||
public void apply_project_visibility_public() { | public void apply_project_visibility_public() { | ||||
OrganizationDto organization = db.organizations().insert(); | OrganizationDto organization = db.organizations().insert(); | ||||
PARAM_VISIBILITY, | PARAM_VISIBILITY, | ||||
PARAM_ORGANIZATION, | PARAM_ORGANIZATION, | ||||
PARAM_NAME, | PARAM_NAME, | ||||
PARAM_PROJECT, | |||||
PARAM_BRANCH); | |||||
PARAM_PROJECT); | |||||
WebService.Param organization = definition.param(PARAM_ORGANIZATION); | WebService.Param organization = definition.param(PARAM_ORGANIZATION); | ||||
assertThat(organization.description()).isEqualTo("The key of the organization"); | assertThat(organization.description()).isEqualTo("The key of the organization"); | ||||
WebService.Param name = definition.param(PARAM_NAME); | WebService.Param name = definition.param(PARAM_NAME); | ||||
assertThat(name.isRequired()).isTrue(); | assertThat(name.isRequired()).isTrue(); | ||||
assertThat(name.description()).isEqualTo("Name of the project. If name is longer than 500, it is abbreviated."); | assertThat(name.description()).isEqualTo("Name of the project. If name is longer than 500, it is abbreviated."); | ||||
WebService.Param branch = definition.param(PARAM_BRANCH); | |||||
assertThat(branch.deprecatedSince()).isNotNull(); | |||||
} | } | ||||
private CreateWsResponse call(CreateRequest request) { | private CreateWsResponse call(CreateRequest request) { | ||||
ofNullable(request.getOrganization()).ifPresent(org -> httpRequest.setParam("organization", org)); | ofNullable(request.getOrganization()).ifPresent(org -> httpRequest.setParam("organization", org)); | ||||
ofNullable(request.getProjectKey()).ifPresent(key -> httpRequest.setParam("project", key)); | ofNullable(request.getProjectKey()).ifPresent(key -> httpRequest.setParam("project", key)); | ||||
ofNullable(request.getName()).ifPresent(name -> httpRequest.setParam("name", name)); | ofNullable(request.getName()).ifPresent(name -> httpRequest.setParam("name", name)); | ||||
ofNullable(request.getBranch()).ifPresent(branch -> httpRequest.setParam("branch", branch)); | |||||
return httpRequest.executeProtobuf(CreateWsResponse.class); | return httpRequest.executeProtobuf(CreateWsResponse.class); | ||||
} | } | ||||
checkArgument(isValidProjectKey(keyCandidate), "Malformed key for '%s'. %s", keyCandidate, "Project key cannot be empty nor contain whitespaces."); | checkArgument(isValidProjectKey(keyCandidate), "Malformed key for '%s'. %s", keyCandidate, "Project key cannot be empty nor contain whitespaces."); | ||||
} | } | ||||
/** | |||||
* <p>Test if given parameter is valid for a branch. Valid format is:</p> | |||||
* <ul> | |||||
* <li>Allowed characters: | |||||
* <ul> | |||||
* <li>Uppercase ASCII letters A-Z</li> | |||||
* <li>Lowercase ASCII letters a-z</li> | |||||
* <li>ASCII digits 0-9</li> | |||||
* <li>Punctuation signs dash '-', underscore '_', period '.', and '/'</li> | |||||
* </ul> | |||||
* </li> | |||||
* </ul> | |||||
* | |||||
* @return <code>true</code> if <code>branchCandidate</code> can be used for a project | |||||
*/ | |||||
public static boolean isValidLegacyBranch(String branchCandidate) { | |||||
return branchCandidate.matches(VALID_BRANCH_REGEXP); | |||||
} | |||||
/** | /** | ||||
* Return the project key with potential branch | * Return the project key with potential branch | ||||
*/ | */ |
assertThat(ComponentKeys.isValidProjectKey("ab ")).isFalse(); | assertThat(ComponentKeys.isValidProjectKey("ab ")).isFalse(); | ||||
} | } | ||||
@Test | |||||
public void isValidBranchKey() { | |||||
assertThat(ComponentKeys.isValidLegacyBranch("")).isTrue(); | |||||
assertThat(ComponentKeys.isValidLegacyBranch("abc")).isTrue(); | |||||
assertThat(ComponentKeys.isValidLegacyBranch("0123")).isTrue(); | |||||
assertThat(ComponentKeys.isValidLegacyBranch("ab 12")).isFalse(); | |||||
assertThat(ComponentKeys.isValidLegacyBranch("ab_12")).isTrue(); | |||||
assertThat(ComponentKeys.isValidLegacyBranch("ab/12")).isTrue(); | |||||
assertThat(ComponentKeys.isValidLegacyBranch("ab\\12")).isFalse(); | |||||
assertThat(ComponentKeys.isValidLegacyBranch("ab\n")).isFalse(); | |||||
} | |||||
@Test | @Test | ||||
public void checkProjectKey_with_correct_keys() { | public void checkProjectKey_with_correct_keys() { | ||||
ComponentKeys.checkProjectKey("abc"); | ComponentKeys.checkProjectKey("abc"); |
private final String name; | private final String name; | ||||
private final String originalName; | private final String originalName; | ||||
private final String description; | private final String description; | ||||
private final String keyWithBranch; | |||||
private final String branch; | |||||
private final Map<String, String> properties; | private final Map<String, String> properties; | ||||
private final String key; | private final String key; | ||||
this.name = definition.getName(); | this.name = definition.getName(); | ||||
this.originalName = definition.getOriginalName(); | this.originalName = definition.getOriginalName(); | ||||
this.description = definition.getDescription(); | this.description = definition.getDescription(); | ||||
this.keyWithBranch = definition.getKeyWithBranch(); | |||||
this.branch = definition.getBranch(); | |||||
this.properties = Collections.unmodifiableMap(new HashMap<>(definition.properties())); | this.properties = Collections.unmodifiableMap(new HashMap<>(definition.properties())); | ||||
this.definition = definition; | this.definition = definition; | ||||
return workingDir; | return workingDir; | ||||
} | } | ||||
/** | |||||
* Module key without branch | |||||
*/ | |||||
@Override | @Override | ||||
public String key() { | public String key() { | ||||
return key; | return key; | ||||
return workDir; | return workDir; | ||||
} | } | ||||
public String getKeyWithBranch() { | |||||
return keyWithBranch; | |||||
} | |||||
@CheckForNull | |||||
public String getBranch() { | |||||
return branch; | |||||
} | |||||
public Map<String, String> properties() { | public Map<String, String> properties() { | ||||
return properties; | return properties; | ||||
} | } |
assertThat(module.key()).isEqualTo("moduleKey"); | assertThat(module.key()).isEqualTo("moduleKey"); | ||||
assertThat(module.definition()).isEqualTo(def); | assertThat(module.definition()).isEqualTo(def); | ||||
assertThat(module.getBranch()).isNull(); | |||||
assertThat(module.getBaseDir()).isEqualTo(baseDir.toPath()); | assertThat(module.getBaseDir()).isEqualTo(baseDir.toPath()); | ||||
assertThat(module.getKeyWithBranch()).isEqualTo("moduleKey"); | |||||
assertThat(module.getWorkDir()).isEqualTo(workDir.toPath()); | assertThat(module.getWorkDir()).isEqualTo(workDir.toPath()); | ||||
assertThat(module.getEncoding()).isEqualTo(Charset.defaultCharset()); | assertThat(module.getEncoding()).isEqualTo(Charset.defaultCharset()); | ||||
assertThat(module.getSourceDirsOrFiles().get()).containsExactlyInAnyOrder(src); | assertThat(module.getSourceDirsOrFiles().get()).containsExactlyInAnyOrder(src); | ||||
assertThat(module.key()).isEqualTo("moduleKey"); | assertThat(module.key()).isEqualTo("moduleKey"); | ||||
assertThat(module.definition()).isEqualTo(def); | assertThat(module.definition()).isEqualTo(def); | ||||
assertThat(module.getBranch()).isNull(); | |||||
assertThat(module.getBaseDir()).isEqualTo(baseDir.toPath()); | assertThat(module.getBaseDir()).isEqualTo(baseDir.toPath()); | ||||
assertThat(module.getKeyWithBranch()).isEqualTo("moduleKey"); | |||||
assertThat(module.getWorkDir()).isEqualTo(workDir.toPath()); | assertThat(module.getWorkDir()).isEqualTo(workDir.toPath()); | ||||
assertThat(module.getEncoding()).isEqualTo(Charset.defaultCharset()); | assertThat(module.getEncoding()).isEqualTo(Charset.defaultCharset()); | ||||
assertThat(module.getSourceDirsOrFiles()).isNotPresent(); | assertThat(module.getSourceDirsOrFiles()).isNotPresent(); |
assertThat(project.getName()).isEqualTo("projectName"); | assertThat(project.getName()).isEqualTo("projectName"); | ||||
assertThat(project.getOriginalName()).isEqualTo("projectName"); | assertThat(project.getOriginalName()).isEqualTo("projectName"); | ||||
assertThat(project.definition()).isEqualTo(def); | assertThat(project.definition()).isEqualTo(def); | ||||
assertThat(project.getBranch()).isNull(); | |||||
assertThat(project.getBaseDir()).isEqualTo(baseDir.toPath()); | assertThat(project.getBaseDir()).isEqualTo(baseDir.toPath()); | ||||
assertThat(project.getKeyWithBranch()).isEqualTo("projectKey"); | |||||
assertThat(project.getDescription()).isEqualTo("desc"); | assertThat(project.getDescription()).isEqualTo("desc"); | ||||
assertThat(project.getWorkDir()).isEqualTo(workDir.toPath()); | assertThat(project.getWorkDir()).isEqualTo(workDir.toPath()); | ||||
assertThat(project.getEncoding()).isEqualTo(Charset.defaultCharset()); | assertThat(project.getEncoding()).isEqualTo(Charset.defaultCharset()); |
/* Global settings */ | /* Global settings */ | ||||
String SONAR_HOME = "SONAR_HOME"; | String SONAR_HOME = "SONAR_HOME"; | ||||
/** | |||||
* @deprecated since 6.7. This feature is deprecated in favor of the new branch feature. | |||||
* @see <a href="https://redirect.sonarsource.com/doc/branches.html">https://redirect.sonarsource.com/doc/branches.html/a> | |||||
*/ | |||||
@Deprecated | |||||
String PROJECT_BRANCH_PROPERTY = "sonar.branch"; | |||||
String PROJECT_VERSION_PROPERTY = "sonar.projectVersion"; | String PROJECT_VERSION_PROPERTY = "sonar.projectVersion"; | ||||
String BUILD_STRING_PROPERTY = "sonar.buildString"; | String BUILD_STRING_PROPERTY = "sonar.buildString"; | ||||
return properties.get(CoreProperties.PROJECT_KEY_PROPERTY); | return properties.get(CoreProperties.PROJECT_KEY_PROPERTY); | ||||
} | } | ||||
/** | |||||
* @since 4.5 | |||||
*/ | |||||
public String getKeyWithBranch() { | |||||
String branch = getBranch(); | |||||
String projectKey = getKey(); | |||||
if (StringUtils.isNotBlank(branch)) { | |||||
projectKey = String.format("%s:%s", projectKey, branch); | |||||
} | |||||
return projectKey; | |||||
} | |||||
@CheckForNull | |||||
public String getBranch() { | |||||
String branch = properties.get(CoreProperties.PROJECT_BRANCH_PROPERTY); | |||||
if (StringUtils.isNotBlank(branch)) { | |||||
return branch; | |||||
} else if (parent != null) { | |||||
return parent.getBranch(); | |||||
} | |||||
return null; | |||||
} | |||||
/** | /** | ||||
* @deprecated since 7.7, use {@link #getOriginalProjectVersion()} instead | * @deprecated since 7.7, use {@link #getOriginalProjectVersion()} instead | ||||
*/ | */ |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU Lesser General Public | |||||
* License as published by the Free Software Foundation; either | |||||
* version 3 of the License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
* Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program; if not, write to the Free Software Foundation, | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
*/ | |||||
package org.sonar.api.batch.bootstrap; | |||||
/** | |||||
* Provides root project key with branch | |||||
* @deprecated since 7.6 | |||||
*/ | |||||
@Deprecated | |||||
@FunctionalInterface | |||||
public interface ProjectKey { | |||||
String get(); | |||||
} |
*/ | */ | ||||
package org.sonar.api.batch.bootstrap; | package org.sonar.api.batch.bootstrap; | ||||
import org.sonar.api.scanner.ScannerSide; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
import org.sonar.api.scanner.ScannerSide; | |||||
/** | /** | ||||
* Mutable project definitions that can be modified by {@link ProjectBuilder} extensions. | * Mutable project definitions that can be modified by {@link ProjectBuilder} extensions. | ||||
public String get() { | public String get() { | ||||
if (root != null) { | if (root != null) { | ||||
return root.getKeyWithBranch(); | |||||
return root.getKey(); | |||||
} | } | ||||
return null; | return null; | ||||
} | } |
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
import javax.annotation.CheckForNull; | |||||
import javax.annotation.concurrent.Immutable; | import javax.annotation.concurrent.Immutable; | ||||
import org.sonar.api.CoreProperties; | |||||
import org.sonar.api.batch.bootstrap.ProjectKey; | |||||
import org.sonar.scanner.scan.ExternalProjectKeyAndOrganization; | import org.sonar.scanner.scan.ExternalProjectKeyAndOrganization; | ||||
import static org.apache.commons.lang.StringUtils.trimToNull; | |||||
import static org.sonar.api.CoreProperties.PROJECT_KEY_PROPERTY; | |||||
/** | /** | ||||
* Properties that are coming from scanner. | * Properties that are coming from scanner. | ||||
*/ | */ | ||||
@Immutable | @Immutable | ||||
public class ProcessedScannerProperties implements ProjectKey { | |||||
public class ProcessedScannerProperties { | |||||
private final Map<String, String> properties; | private final Map<String, String> properties; | ||||
this.properties.putAll(rawScannerProperties.properties()); | this.properties.putAll(rawScannerProperties.properties()); | ||||
externalProjectKeyAndOrganization.getProjectKey() | externalProjectKeyAndOrganization.getProjectKey() | ||||
.ifPresent(projectKey -> properties.put(CoreProperties.PROJECT_KEY_PROPERTY, projectKey)); | |||||
.ifPresent(projectKey -> properties.put(PROJECT_KEY_PROPERTY, projectKey)); | |||||
externalProjectKeyAndOrganization.getOrganization() | externalProjectKeyAndOrganization.getOrganization() | ||||
.ifPresent(organization -> properties.put(org.sonar.core.config.ScannerProperties.ORGANIZATION, organization)); | .ifPresent(organization -> properties.put(org.sonar.core.config.ScannerProperties.ORGANIZATION, organization)); | ||||
} | } | ||||
return properties.get(key); | return properties.get(key); | ||||
} | } | ||||
@Override | |||||
public String get() { | |||||
return getKeyWithBranch(); | |||||
} | |||||
private String getKey() { | |||||
return properties.get(CoreProperties.PROJECT_KEY_PROPERTY); | |||||
} | |||||
public String getKeyWithBranch() { | |||||
String branch = getBranch(); | |||||
String projectKey = getKey(); | |||||
if (branch == null) { | |||||
return projectKey; | |||||
} | |||||
return String.format("%s:%s", projectKey, branch); | |||||
} | |||||
@CheckForNull | |||||
private String getBranch() { | |||||
return trimToNull(properties.get(CoreProperties.PROJECT_BRANCH_PROPERTY)); | |||||
public String getProjectKey() { | |||||
return this.properties.get(PROJECT_KEY_PROPERTY); | |||||
} | } | ||||
} | } |
*/ | */ | ||||
package org.sonar.scanner.cpd; | package org.sonar.scanner.cpd; | ||||
import org.apache.commons.lang.StringUtils; | |||||
import org.sonar.api.CoreProperties; | import org.sonar.api.CoreProperties; | ||||
import org.sonar.api.config.Configuration; | import org.sonar.api.config.Configuration; | ||||
import org.sonar.duplications.block.BlockChunker; | import org.sonar.duplications.block.BlockChunker; | ||||
public class CpdSettings { | public class CpdSettings { | ||||
private final Configuration settings; | private final Configuration settings; | ||||
private final String branch; | |||||
public CpdSettings(Configuration config, DefaultInputProject project) { | |||||
public CpdSettings(Configuration config) { | |||||
this.settings = config; | this.settings = config; | ||||
this.branch = project.getBranch(); | |||||
} | } | ||||
public boolean isCrossProjectDuplicationEnabled() { | public boolean isCrossProjectDuplicationEnabled() { | ||||
return settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT).orElse(false) | |||||
// No cross project duplication for branches | |||||
&& StringUtils.isBlank(branch); | |||||
return settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT).orElse(false); | |||||
} | } | ||||
/** | /** |
@CheckForNull | @CheckForNull | ||||
private static String getName(AbstractProjectOrModule module) { | private static String getName(AbstractProjectOrModule module) { | ||||
if (StringUtils.isNotEmpty(module.definition().getBranch())) { | |||||
return module.definition().getName() + " " + module.definition().getBranch(); | |||||
} else { | |||||
return module.definition().getOriginalName(); | |||||
} | |||||
return module.definition().getOriginalName(); | |||||
} | } | ||||
@CheckForNull | @CheckForNull |
import org.sonar.scanner.scm.ScmConfiguration; | import org.sonar.scanner.scm.ScmConfiguration; | ||||
import org.sonar.scanner.scm.ScmRevision; | import org.sonar.scanner.scm.ScmRevision; | ||||
import static java.util.Optional.ofNullable; | |||||
public class MetadataPublisher implements ReportPublisherStep { | public class MetadataPublisher implements ReportPublisherStep { | ||||
private static final Logger LOG = Loggers.get(MetadataPublisher.class); | private static final Logger LOG = Loggers.get(MetadataPublisher.class); | ||||
addBranchInformation(builder); | addBranchInformation(builder); | ||||
} | } | ||||
ofNullable(rootProject.getBranch()).ifPresent(builder::setDeprecatedBranch); | |||||
addScmInformation(builder); | addScmInformation(builder); | ||||
for (QProfile qp : qProfiles.findAll()) { | for (QProfile qp : qProfiles.findAll()) { |
.setParam("organization", properties.organizationKey().orElse(null)) | .setParam("organization", properties.organizationKey().orElse(null)) | ||||
.setParam("projectKey", moduleHierarchy.root().key()) | .setParam("projectKey", moduleHierarchy.root().key()) | ||||
.setParam("projectName", moduleHierarchy.root().getOriginalName()) | .setParam("projectName", moduleHierarchy.root().getOriginalName()) | ||||
.setParam("projectBranch", moduleHierarchy.root().getBranch()) | |||||
.setPart("report", filePart); | .setPart("report", filePart); | ||||
String branchName = branchConfiguration.branchName(); | String branchName = branchConfiguration.branchName(); | ||||
} else { | } else { | ||||
Map<String, String> metadata = new LinkedHashMap<>(); | Map<String, String> metadata = new LinkedHashMap<>(); | ||||
String effectiveKey = moduleHierarchy.root().getKeyWithBranch(); | |||||
properties.organizationKey().ifPresent(org -> metadata.put("organization", org)); | properties.organizationKey().ifPresent(org -> metadata.put("organization", org)); | ||||
metadata.put("projectKey", effectiveKey); | |||||
metadata.put("projectKey", moduleHierarchy.root().key()); | |||||
metadata.put("serverUrl", server.getPublicRootUrl()); | metadata.put("serverUrl", server.getPublicRootUrl()); | ||||
metadata.put("serverVersion", server.getVersion()); | metadata.put("serverVersion", server.getVersion()); | ||||
properties.branch().ifPresent(branch -> metadata.put("branch", branch)); | properties.branch().ifPresent(branch -> metadata.put("branch", branch)); | ||||
URL dashboardUrl = buildDashboardUrl(server.getPublicRootUrl(), effectiveKey); | |||||
URL dashboardUrl = buildDashboardUrl(server.getPublicRootUrl(), moduleHierarchy.root().key()); | |||||
metadata.put("dashboardUrl", dashboardUrl.toExternalForm()); | metadata.put("dashboardUrl", dashboardUrl.toExternalForm()); | ||||
URL taskUrl = HttpUrl.parse(server.getPublicRootUrl()).newBuilder() | URL taskUrl = HttpUrl.parse(server.getPublicRootUrl()).newBuilder() |
public ProjectRepositories get() { | public ProjectRepositories get() { | ||||
if (project == null) { | if (project == null) { | ||||
Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); | Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); | ||||
project = loader.load(scannerProperties.getKeyWithBranch(), branchConfig.longLivingSonarReferenceBranch()); | |||||
project = loader.load(scannerProperties.getProjectKey(), branchConfig.longLivingSonarReferenceBranch()); | |||||
profiler.stopInfo(); | profiler.stopInfo(); | ||||
} | } | ||||
public QualityProfiles provide(QualityProfileLoader loader, ProcessedScannerProperties props) { | public QualityProfiles provide(QualityProfileLoader loader, ProcessedScannerProperties props) { | ||||
if (this.profiles == null) { | if (this.profiles == null) { | ||||
Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); | Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); | ||||
profiles = new QualityProfiles(loader.load(props.getKeyWithBranch())); | |||||
profiles = new QualityProfiles(loader.load(props.getProjectKey())); | |||||
profiler.stopInfo(); | profiler.stopInfo(); | ||||
} | } | ||||
@Override | @Override | ||||
public Map<String, String> loadProjectSettings() { | public Map<String, String> loadProjectSettings() { | ||||
return load(scannerProperties.getKeyWithBranch()); | |||||
return load(scannerProperties.getProjectKey()); | |||||
} | } | ||||
} | } |
validateModule(moduleDef, validationMessages); | validateModule(moduleDef, validationMessages); | ||||
} | } | ||||
String deprecatedBranchName = reactor.getRoot().getBranch(); | |||||
if (isBranchFeatureAvailable()) { | if (isBranchFeatureAvailable()) { | ||||
branchParamsValidator.validate(validationMessages, deprecatedBranchName); | |||||
branchParamsValidator.validate(validationMessages); | |||||
} else { | } else { | ||||
validateBranchParamsWhenPluginAbsent(validationMessages); | validateBranchParamsWhenPluginAbsent(validationMessages); | ||||
validatePullRequestParamsWhenPluginAbsent(validationMessages); | validatePullRequestParamsWhenPluginAbsent(validationMessages); | ||||
} | } | ||||
validateLegacyBranch(validationMessages, deprecatedBranchName); | |||||
if (!validationMessages.isEmpty()) { | if (!validationMessages.isEmpty()) { | ||||
throw MessageException.of("Validation of project reactor failed:\n o " + | throw MessageException.of("Validation of project reactor failed:\n o " + | ||||
String.join("\n o ", validationMessages)); | String.join("\n o ", validationMessages)); | ||||
} | } | ||||
} | } | ||||
private static void validateLegacyBranch(List<String> validationMessages, @Nullable String branch) { | |||||
if (isNotEmpty(branch) && !ComponentKeys.isValidLegacyBranch(branch)) { | |||||
validationMessages.add(format("\"%s\" is not a valid branch name. " | |||||
+ "Allowed characters are alphanumeric, '-', '_', '.' and '/'.", branch)); | |||||
} | |||||
} | |||||
private boolean isBranchFeatureAvailable() { | private boolean isBranchFeatureAvailable() { | ||||
return branchParamsValidator != null; | return branchParamsValidator != null; | ||||
} | } |
GlobalAnalysisMode analysisMode = getComponentByType(GlobalAnalysisMode.class); | GlobalAnalysisMode analysisMode = getComponentByType(GlobalAnalysisMode.class); | ||||
InputModuleHierarchy tree = getComponentByType(InputModuleHierarchy.class); | InputModuleHierarchy tree = getComponentByType(InputModuleHierarchy.class); | ||||
ScanProperties properties = getComponentByType(ScanProperties.class); | ScanProperties properties = getComponentByType(ScanProperties.class); | ||||
SonarRuntime sonarRuntime = getComponentByType(SonarRuntime.class); | |||||
properties.validate(); | properties.validate(); | ||||
properties.organizationKey().ifPresent(k -> LOG.info("Organization key: {}", k)); | properties.organizationKey().ifPresent(k -> LOG.info("Organization key: {}", k)); | ||||
if (sonarRuntime.getEdition() == SonarEdition.SONARCLOUD) { | |||||
String branch = tree.root().definition().getBranch(); | |||||
if (branch != null) { | |||||
LOG.info("Branch key: {}", branch); | |||||
LOG.warn("The use of \"sonar.branch\" is deprecated and replaced by \"{}\". See {}.", | |||||
ScannerProperties.BRANCH_NAME, ScannerProperties.BRANCHES_DOC_LINK); | |||||
} | |||||
} else { | |||||
properties.get("sonar.branch").ifPresent(deprecatedBranch -> { | |||||
throw MessageException.of("The 'sonar.branch' parameter is no longer supported. You should stop using it. " + | |||||
"Branch analysis is available in Developer Edition and above. See https://redirect.sonarsource.com/editions/developer.html for more information."); | |||||
}); | |||||
} | |||||
properties.get("sonar.branch").ifPresent(deprecatedBranch -> { | |||||
throw MessageException.of("The 'sonar.branch' parameter is no longer supported. You should stop using it. " + | |||||
"Branch analysis is available in Developer Edition and above. See https://redirect.sonarsource.com/editions/developer.html for more information."); | |||||
}); | |||||
BranchConfiguration branchConfig = getComponentByType(BranchConfiguration.class); | BranchConfiguration branchConfig = getComponentByType(BranchConfiguration.class); | ||||
if (branchConfig.branchType() == BranchType.PULL_REQUEST) { | if (branchConfig.branchType() == BranchType.PULL_REQUEST) { |
@ScannerSide | @ScannerSide | ||||
public interface BranchParamsValidator { | public interface BranchParamsValidator { | ||||
void validate(List<String> validationMessages, @Nullable String deprecatedBranchName); | |||||
void validate(List<String> validationMessages); | |||||
} | } |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2019 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU Lesser General Public | |||||
* License as published by the Free Software Foundation; either | |||||
* version 3 of the License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
* Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program; if not, write to the Free Software Foundation, | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
*/ | |||||
package org.sonar.scanner.scan.branch; | |||||
import java.util.List; | |||||
import javax.annotation.Nullable; | |||||
public class DefaultBranchParamsValidator implements BranchParamsValidator { | |||||
@Override | |||||
public void validate(List<String> validationMessages, @Nullable String deprecatedBranchName) { | |||||
// no-op | |||||
} | |||||
} |
*/ | */ | ||||
package org.sonar.scanner.scan.branch; | package org.sonar.scanner.scan.branch; | ||||
import java.util.Collections; | |||||
import com.google.common.collect.ImmutableList; | |||||
import org.picocontainer.annotations.Nullable; | import org.picocontainer.annotations.Nullable; | ||||
import org.picocontainer.injectors.ProviderAdapter; | import org.picocontainer.injectors.ProviderAdapter; | ||||
import org.sonar.api.batch.bootstrap.ProjectKey; | |||||
import org.sonar.api.utils.log.Logger; | import org.sonar.api.utils.log.Logger; | ||||
import org.sonar.api.utils.log.Loggers; | import org.sonar.api.utils.log.Loggers; | ||||
import org.sonar.api.utils.log.Profiler; | import org.sonar.api.utils.log.Profiler; | ||||
import org.sonar.scanner.bootstrap.ProcessedScannerProperties; | |||||
public class ProjectBranchesProvider extends ProviderAdapter { | public class ProjectBranchesProvider extends ProviderAdapter { | ||||
private ProjectBranches branches = null; | private ProjectBranches branches = null; | ||||
public ProjectBranches provide(@Nullable ProjectBranchesLoader loader, ProjectKey projectKey) { | |||||
if (branches != null) { | |||||
return branches; | |||||
public ProjectBranches provide(@Nullable ProjectBranchesLoader loader, ProcessedScannerProperties scannerProperties) { | |||||
if (this.branches != null) { | |||||
return this.branches; | |||||
} | } | ||||
if (loader == null) { | if (loader == null) { | ||||
branches = new ProjectBranches(Collections.emptyList()); | |||||
return branches; | |||||
this.branches = new ProjectBranches(ImmutableList.of()); | |||||
return this.branches; | |||||
} | } | ||||
Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); | Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); | ||||
branches = loader.load(projectKey.get()); | |||||
this.branches = loader.load(scannerProperties.getProjectKey()); | |||||
profiler.stopInfo(); | profiler.stopInfo(); | ||||
return branches; | |||||
return this.branches; | |||||
} | } | ||||
} | } |
import java.util.Collections; | import java.util.Collections; | ||||
import org.picocontainer.injectors.ProviderAdapter; | import org.picocontainer.injectors.ProviderAdapter; | ||||
import org.sonar.api.batch.bootstrap.ProjectKey; | |||||
import org.sonar.api.utils.log.Logger; | import org.sonar.api.utils.log.Logger; | ||||
import org.sonar.api.utils.log.Loggers; | import org.sonar.api.utils.log.Loggers; | ||||
import org.sonar.api.utils.log.Profiler; | import org.sonar.api.utils.log.Profiler; | ||||
import org.sonar.scanner.bootstrap.ProcessedScannerProperties; | |||||
public class ProjectPullRequestsProvider extends ProviderAdapter { | public class ProjectPullRequestsProvider extends ProviderAdapter { | ||||
private ProjectPullRequests pullRequests = null; | private ProjectPullRequests pullRequests = null; | ||||
public ProjectPullRequests provide(@org.picocontainer.annotations.Nullable ProjectPullRequestsLoader loader, ProjectKey projectKey) { | |||||
public ProjectPullRequests provide(@org.picocontainer.annotations.Nullable ProjectPullRequestsLoader loader, ProcessedScannerProperties scannerProperties) { | |||||
if (pullRequests != null) { | if (pullRequests != null) { | ||||
return pullRequests; | return pullRequests; | ||||
} | } | ||||
} | } | ||||
Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); | Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); | ||||
pullRequests = loader.load(projectKey.get()); | |||||
pullRequests = loader.load(scannerProperties.getProjectKey()); | |||||
profiler.stopInfo(); | profiler.stopInfo(); | ||||
return pullRequests; | return pullRequests; | ||||
} | } |
projectRelativePath.toString(), | projectRelativePath.toString(), | ||||
moduleRelativePath.toString(), | moduleRelativePath.toString(), | ||||
type, language, scannerComponentIdGenerator.getAsInt(), sensorStrategy); | type, language, scannerComponentIdGenerator.getAsInt(), sensorStrategy); | ||||
DefaultInputFile inputFile = new DefaultInputFile(indexedFile, f -> metadataGenerator.setMetadata(module.getKeyWithBranch(), f, module.getEncoding())); | |||||
DefaultInputFile inputFile = new DefaultInputFile(indexedFile, f -> metadataGenerator.setMetadata(module.key(), f, module.getEncoding())); | |||||
if (language != null) { | if (language != null) { | ||||
inputFile.setPublished(true); | inputFile.setPublished(true); | ||||
} | } |
if (configuration.forceReloadAll() || f.status() != Status.SAME) { | if (configuration.forceReloadAll() || f.status() != Status.SAME) { | ||||
addIfNotEmpty(filesToBlame, f); | addIfNotEmpty(filesToBlame, f); | ||||
} else if (!branchConfiguration.isShortOrPullRequest()) { | } else if (!branchConfiguration.isShortOrPullRequest()) { | ||||
FileData fileData = projectRepositoriesSupplier.get().fileData(componentStore.findModule(f).getKeyWithBranch(), f); | |||||
FileData fileData = projectRepositoriesSupplier.get().fileData(componentStore.findModule(f).key(), f); | |||||
if (fileData == null || StringUtils.isEmpty(fileData.revision())) { | if (fileData == null || StringUtils.isEmpty(fileData.revision())) { | ||||
addIfNotEmpty(filesToBlame, f); | addIfNotEmpty(filesToBlame, f); | ||||
} else { | } else { |
public class CpdSettingsTest { | public class CpdSettingsTest { | ||||
private CpdSettings cpdSettings; | private CpdSettings cpdSettings; | ||||
private Configuration configuration; | private Configuration configuration; | ||||
private DefaultInputProject project; | |||||
@Before | @Before | ||||
public void setUp() { | public void setUp() { | ||||
project = mock(DefaultInputProject.class); | |||||
configuration = mock(Configuration.class); | configuration = mock(Configuration.class); | ||||
cpdSettings = new CpdSettings(configuration, project); | |||||
cpdSettings = new CpdSettings(configuration); | |||||
} | } | ||||
@Test | @Test |
import com.google.common.collect.ImmutableMap; | import com.google.common.collect.ImmutableMap; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.nio.file.Files; | |||||
import java.nio.file.Path; | |||||
import java.util.Map; | import java.util.Map; | ||||
import org.apache.commons.io.FileUtils; | import org.apache.commons.io.FileUtils; | ||||
import org.junit.Before; | import org.junit.Before; | ||||
import org.sonar.api.utils.MessageException; | import org.sonar.api.utils.MessageException; | ||||
import org.sonar.scanner.mediumtest.AnalysisResult; | import org.sonar.scanner.mediumtest.AnalysisResult; | ||||
import org.sonar.scanner.mediumtest.ScannerMediumTester; | import org.sonar.scanner.mediumtest.ScannerMediumTester; | ||||
import org.sonar.api.utils.MessageException; | |||||
import org.sonar.scanner.mediumtest.ScannerMediumTester; | |||||
import org.sonar.xoo.XooPlugin; | import org.sonar.xoo.XooPlugin; | ||||
import org.sonar.xoo.rule.XooRulesDefinition; | import org.sonar.xoo.rule.XooRulesDefinition; | ||||
import static org.assertj.core.api.Assertions.assertThat; | |||||
public class DeprecatedBranchMediumTest { | public class DeprecatedBranchMediumTest { | ||||
@Rule | @Rule | ||||
.addActiveRule("xoo", "xoo:OneIssuePerFile", null, "One Issue Per File", null, null, null) | .addActiveRule("xoo", "xoo:OneIssuePerFile", null, "One Issue Per File", null, null, null) | ||||
.addDefaultQProfile("xoo", "Sonar Way"); | .addDefaultQProfile("xoo", "Sonar Way"); | ||||
@Rule | |||||
public ScannerMediumTester testerSC = new ScannerMediumTester() | |||||
.setEdition(SonarEdition.SONARCLOUD) | |||||
.registerPlugin("xoo", new XooPlugin()) | |||||
.addRules(new XooRulesDefinition()) | |||||
// active a rule just to be sure that xoo files are published | |||||
.addActiveRule("xoo", "xoo:OneIssuePerFile", null, "One Issue Per File", null, null, null) | |||||
.addDefaultQProfile("xoo", "Sonar Way"); | |||||
private File baseDir; | private File baseDir; | ||||
private Map<String, String> commonProps; | private Map<String, String> commonProps; | ||||
@Before | @Before | ||||
public void prepare() throws IOException { | |||||
public void prepare() { | |||||
baseDir = temp.getRoot(); | baseDir = temp.getRoot(); | ||||
commonProps = ImmutableMap.<String, String>builder() | commonProps = ImmutableMap.<String, String>builder() | ||||
} | } | ||||
@Test | @Test | ||||
public void scanProjectWithBranchOnSonarQube() throws IOException { | |||||
public void scanProjectWithBranch() throws IOException { | |||||
File srcDir = new File(baseDir, "src"); | File srcDir = new File(baseDir, "src"); | ||||
srcDir.mkdir(); | srcDir.mkdir(); | ||||
.build()) | .build()) | ||||
.execute(); | .execute(); | ||||
} | } | ||||
@Test | |||||
public void scanProjectWithBranchOnSonarCloud() throws IOException { | |||||
File srcDir = new File(baseDir, "src"); | |||||
srcDir.mkdir(); | |||||
File xooFile = new File(srcDir, "sample.xoo"); | |||||
FileUtils.write(xooFile, "Sample xoo\ncontent"); | |||||
AnalysisResult result = testerSC.newAnalysis() | |||||
.properties(ImmutableMap.<String, String>builder() | |||||
.putAll(commonProps) | |||||
.put("sonar.branch", "branch") | |||||
.build()) | |||||
.execute(); | |||||
assertThat(result.inputFiles()).hasSize(1); | |||||
assertThat(result.inputFile("src/sample.xoo").key()).isEqualTo("com.foo.project:src/sample.xoo"); | |||||
DefaultInputFile inputfile = (DefaultInputFile) result.inputFile("src/sample.xoo"); | |||||
assertThat(result.getReportReader().readComponent(inputfile.scannerId()).getProjectRelativePath()).isEqualTo("src/sample.xoo"); | |||||
assertThat(result.getReportReader().readMetadata().getDeprecatedBranch()).isEqualTo("branch"); | |||||
result = testerSC.newAnalysis() | |||||
.properties(ImmutableMap.<String, String>builder() | |||||
.putAll(commonProps) | |||||
.put("sonar.branch", "") | |||||
.build()) | |||||
.execute(); | |||||
assertThat(result.inputFiles()).hasSize(1); | |||||
assertThat(result.inputFile("src/sample.xoo").key()).isEqualTo("com.foo.project:src/sample.xoo"); | |||||
} | |||||
@Test | |||||
public void scanMultiModuleWithBranchOnSonarCloud() throws IOException { | |||||
Path srcDir = baseDir.toPath().resolve("moduleA").resolve("src"); | |||||
Files.createDirectories(srcDir); | |||||
File xooFile = new File(srcDir.toFile(), "sample.xoo"); | |||||
FileUtils.write(xooFile, "Sample xoo\ncontent"); | |||||
AnalysisResult result = testerSC.newAnalysis() | |||||
.properties(ImmutableMap.<String, String>builder() | |||||
.putAll(commonProps) | |||||
.put("sonar.branch", "branch") | |||||
.put("sonar.modules", "moduleA") | |||||
.build()) | |||||
.execute(); | |||||
assertThat(result.inputFiles()).hasSize(1); | |||||
assertThat(result.inputFile("moduleA/src/sample.xoo").key()).isEqualTo("com.foo.project:moduleA/src/sample.xoo"); | |||||
// no branch in the report | |||||
DefaultInputFile inputfile = (DefaultInputFile) result.inputFile("moduleA/src/sample.xoo"); | |||||
assertThat(result.getReportReader().readComponent(inputfile.scannerId()).getProjectRelativePath()).isEqualTo("moduleA/src/sample.xoo"); | |||||
assertThat(result.getReportReader().readMetadata().getDeprecatedBranch()).isEqualTo("branch"); | |||||
result = testerSC.newAnalysis() | |||||
.properties(ImmutableMap.<String, String>builder() | |||||
.putAll(commonProps) | |||||
.put("sonar.branch", "") | |||||
.put("sonar.modules", "moduleA") | |||||
.build()) | |||||
.execute(); | |||||
assertThat(result.inputFiles()).hasSize(1); | |||||
assertThat(result.inputFile("moduleA/src/sample.xoo").key()).isEqualTo("com.foo.project:moduleA/src/sample.xoo"); | |||||
} | |||||
} | } |
assertThat(result.getReportComponent(file.scannerId())).isNotNull(); | assertThat(result.getReportComponent(file.scannerId())).isNotNull(); | ||||
} | } | ||||
@Test | |||||
public void logProjectKeyAndOrganizationKey() throws IOException { | |||||
builder.put("sonar.organization", "my org"); | |||||
builder.put("sonar.branch", ""); | |||||
File srcDir = new File(baseDir, "src"); | |||||
srcDir.mkdir(); | |||||
File xooFile = new File(srcDir, "sample.xoo"); | |||||
FileUtils.write(xooFile, "Sample xoo\ncontent", StandardCharsets.UTF_8); | |||||
tester.newAnalysis() | |||||
.properties(builder | |||||
.put("sonar.sources", "src") | |||||
.build()) | |||||
.execute(); | |||||
assertThat(logTester.logs()).contains("Project key: com.foo.project"); | |||||
assertThat(logTester.logs()).contains("Organization key: my org"); | |||||
assertThat(logTester.logs().stream().collect(joining("\n"))).doesNotContain("Branch key"); | |||||
} | |||||
@Test | |||||
public void logBranchKey() throws IOException { | |||||
builder.put("sonar.branch", "my-branch"); | |||||
File srcDir = new File(baseDir, "src"); | |||||
assertThat(srcDir.mkdir()).isTrue(); | |||||
File xooFile = new File(srcDir, "sample.xoo"); | |||||
FileUtils.write(xooFile, "Sample xoo\ncontent", StandardCharsets.UTF_8); | |||||
tester.newAnalysis() | |||||
.properties(builder | |||||
.put("sonar.sources", "src") | |||||
.build()) | |||||
.execute(); | |||||
assertThat(logTester.logs()).contains("Project key: com.foo.project"); | |||||
assertThat(logTester.logs()).contains("Branch key: my-branch"); | |||||
assertThat(logTester.logs()) | |||||
.contains("The use of \"sonar.branch\" is deprecated and replaced by \"sonar.branch.name\". See https://redirect.sonarsource.com/doc/branches.html."); | |||||
} | |||||
@Test | @Test | ||||
public void logBranchNameAndType() { | public void logBranchNameAndType() { | ||||
builder.put("sonar.branch.name", "my-branch"); | builder.put("sonar.branch.name", "my-branch"); |
} | } | ||||
@Test | |||||
// SONAR-6976 | |||||
public void testProjectBuilderWithNewLine() throws IOException { | |||||
File baseDir = prepareProject(); | |||||
exception.expect(MessageException.class); | |||||
exception.expectMessage("is not a valid branch name"); | |||||
tester.newAnalysis() | |||||
.properties(ImmutableMap.<String, String>builder() | |||||
.put("sonar.projectBaseDir", baseDir.getAbsolutePath()) | |||||
.put("sonar.projectKey", "com.foo.project") | |||||
.put("sonar.branch", "branch\n") | |||||
.put("sonar.sources", ".") | |||||
.put("sonar.xoo.enableProjectBuilder", "true") | |||||
.build()) | |||||
.execute(); | |||||
} | |||||
@Test | |||||
public void testProjectBuilderWithBranch() throws IOException { | |||||
File baseDir = prepareProject(); | |||||
AnalysisResult result = tester.newAnalysis() | |||||
.properties(ImmutableMap.<String, String>builder() | |||||
.put("sonar.projectBaseDir", baseDir.getAbsolutePath()) | |||||
.put("sonar.projectKey", "com.foo.project") | |||||
.put("sonar.branch", "my-branch") | |||||
.put("sonar.sources", ".") | |||||
.put("sonar.xoo.enableProjectBuilder", "true") | |||||
.build()) | |||||
.execute(); | |||||
List<Issue> issues = result.issuesFor(result.inputFile("module1/src/sample.xoo")); | |||||
assertThat(issues).hasSize(10); | |||||
assertThat(issues) | |||||
.extracting("msg", "textRange.startLine", "gap") | |||||
.contains(tuple("This issue is generated on each line", 1, 0.0)); | |||||
} | |||||
private File prepareProject() throws IOException { | private File prepareProject() throws IOException { | ||||
File baseDir = temp.newFolder(); | File baseDir = temp.newFolder(); | ||||
File module1Dir = new File(baseDir, "module1"); | File module1Dir = new File(baseDir, "module1"); |
// revision,author,dateTime | // revision,author,dateTime | ||||
"1,foo,2013-01-04\n" + | "1,foo,2013-01-04\n" + | ||||
"1,bar,2013-01-04\n" + | "1,bar,2013-01-04\n" + | ||||
"2,biz,2014-01-04\n", StandardCharsets.UTF_8); | |||||
"2,biz,2014-01-04\n", | |||||
StandardCharsets.UTF_8); | |||||
File sameContentScmOnServer = new File(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO); | File sameContentScmOnServer = new File(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO); | ||||
FileUtils.write(sameContentScmOnServer, SAMPLE_XOO_CONTENT, StandardCharsets.UTF_8); | FileUtils.write(sameContentScmOnServer, SAMPLE_XOO_CONTENT, StandardCharsets.UTF_8); | ||||
FileUtils.write(xooScmFile, | FileUtils.write(xooScmFile, | ||||
// revision,author,dateTime | // revision,author,dateTime | ||||
"1,foo,2013-01-04\n" + | "1,foo,2013-01-04\n" + | ||||
"1,bar,2013-01-04\n", StandardCharsets.UTF_8); | |||||
"1,bar,2013-01-04\n", | |||||
StandardCharsets.UTF_8); | |||||
tester.newAnalysis() | tester.newAnalysis() | ||||
.properties(ImmutableMap.<String, String>builder() | .properties(ImmutableMap.<String, String>builder() | ||||
assertThat(logTester.logs()).containsSubsequence(MISSING_BLAME_INFORMATION_FOR_THE_FOLLOWING_FILES, " * src/no_blame_scm_on_server.xoo"); | assertThat(logTester.logs()).containsSubsequence(MISSING_BLAME_INFORMATION_FOR_THE_FOLLOWING_FILES, " * src/no_blame_scm_on_server.xoo"); | ||||
} | } | ||||
@Test | |||||
public void optimize_blame_for_deprecated_branch() throws IOException, URISyntaxException { | |||||
File baseDir = prepareProject(); | |||||
File changedContentScmOnServer = new File(baseDir, CHANGED_CONTENT_SCM_ON_SERVER_XOO); | |||||
FileUtils.write(changedContentScmOnServer, SAMPLE_XOO_CONTENT + "\nchanged", StandardCharsets.UTF_8); | |||||
File xooScmFile = new File(baseDir, CHANGED_CONTENT_SCM_ON_SERVER_XOO + ".scm"); | |||||
FileUtils.write(xooScmFile, | |||||
// revision,author,dateTime | |||||
"1,foo,2013-01-04\n" + | |||||
"1,bar,2013-01-04\n" + | |||||
"2,biz,2014-01-04\n", StandardCharsets.UTF_8); | |||||
File sameContentScmOnServer = new File(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO); | |||||
FileUtils.write(sameContentScmOnServer, SAMPLE_XOO_CONTENT, StandardCharsets.UTF_8); | |||||
// No need to write .scm file since this file should not be blamed | |||||
File noBlameScmOnServer = new File(baseDir, NO_BLAME_SCM_ON_SERVER_XOO); | |||||
FileUtils.write(noBlameScmOnServer, SAMPLE_XOO_CONTENT + "\nchanged", StandardCharsets.UTF_8); | |||||
// No .scm file to emulate a failure during blame | |||||
File sameContentNoScmOnServer = new File(baseDir, SAME_CONTENT_NO_SCM_ON_SERVER_XOO); | |||||
FileUtils.write(sameContentNoScmOnServer, SAMPLE_XOO_CONTENT, StandardCharsets.UTF_8); | |||||
xooScmFile = new File(baseDir, SAME_CONTENT_NO_SCM_ON_SERVER_XOO + ".scm"); | |||||
FileUtils.write(xooScmFile, | |||||
// revision,author,dateTime | |||||
"1,foo,2013-01-04\n" + | |||||
"1,bar,2013-01-04\n", StandardCharsets.UTF_8); | |||||
tester.newAnalysis() | |||||
.properties(ImmutableMap.<String, String>builder() | |||||
.put("sonar.projectBaseDir", baseDir.getAbsolutePath()) | |||||
.put("sonar.projectKey", "com.foo.project") | |||||
.put("sonar.branch", "mybranch") | |||||
.put("sonar.sources", "src") | |||||
.put("sonar.scm.provider", "xoo") | |||||
.build()) | |||||
.execute(); | |||||
assertThat(getChangesets(baseDir, "src/sample.xoo")).isNotNull(); | |||||
assertThat(getChangesets(baseDir, CHANGED_CONTENT_SCM_ON_SERVER_XOO).getCopyFromPrevious()).isFalse(); | |||||
assertThat(getChangesets(baseDir, SAME_CONTENT_SCM_ON_SERVER_XOO).getCopyFromPrevious()).isTrue(); | |||||
assertThat(getChangesets(baseDir, SAME_CONTENT_NO_SCM_ON_SERVER_XOO).getCopyFromPrevious()).isFalse(); | |||||
assertThat(getChangesets(baseDir, NO_BLAME_SCM_ON_SERVER_XOO)).isNull(); | |||||
// 5 .xoo files + 3 .scm files, but only 4 marked for publishing. 1 file is SAME so not included in the total | |||||
assertThat(logTester.logs()).containsSubsequence("8 files indexed"); | |||||
assertThat(logTester.logs()).containsSubsequence("SCM Publisher 4 source files to be analyzed"); | |||||
assertThat(logTester.logs().stream().anyMatch(s -> Pattern.matches("SCM Publisher 3/4 source files have been analyzed \\(done\\) \\| time=[0-9]+ms", s))).isTrue(); | |||||
assertThat(logTester.logs()).containsSubsequence(MISSING_BLAME_INFORMATION_FOR_THE_FOLLOWING_FILES, " * src/no_blame_scm_on_server.xoo"); | |||||
} | |||||
@Test | @Test | ||||
public void forceReload() throws IOException, URISyntaxException { | public void forceReload() throws IOException, URISyntaxException { | ||||
FileUtils.write(xooScmFile, | FileUtils.write(xooScmFile, | ||||
// revision,author,dateTime | // revision,author,dateTime | ||||
"1,foo,2013-01-04\n" + | "1,foo,2013-01-04\n" + | ||||
"1,bar,2013-01-04\n", StandardCharsets.UTF_8); | |||||
"1,bar,2013-01-04\n", | |||||
StandardCharsets.UTF_8); | |||||
AnalysisBuilder analysisBuilder = tester.newAnalysis() | AnalysisBuilder analysisBuilder = tester.newAnalysis() | ||||
.properties(ImmutableMap.<String, String>builder() | .properties(ImmutableMap.<String, String>builder() |
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.nio.file.Path; | import java.nio.file.Path; | ||||
import java.util.Collections; | |||||
import org.junit.Before; | import org.junit.Before; | ||||
import org.junit.Rule; | import org.junit.Rule; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.sonar.api.batch.fs.internal.DefaultInputProject; | import org.sonar.api.batch.fs.internal.DefaultInputProject; | ||||
import org.sonar.api.batch.fs.internal.TestInputFileBuilder; | import org.sonar.api.batch.fs.internal.TestInputFileBuilder; | ||||
import org.sonar.scanner.protocol.output.FileStructure; | import org.sonar.scanner.protocol.output.FileStructure; | ||||
import org.sonar.scanner.protocol.output.ScannerReport; | |||||
import org.sonar.scanner.protocol.output.ScannerReport.Component; | import org.sonar.scanner.protocol.output.ScannerReport.Component; | ||||
import org.sonar.scanner.protocol.output.ScannerReport.Component.FileStatus; | import org.sonar.scanner.protocol.output.ScannerReport.Component.FileStatus; | ||||
import org.sonar.scanner.protocol.output.ScannerReport.ComponentLink.ComponentLinkType; | import org.sonar.scanner.protocol.output.ScannerReport.ComponentLink.ComponentLinkType; | ||||
private File outputDir; | private File outputDir; | ||||
private ScannerReportWriter writer; | private ScannerReportWriter writer; | ||||
private ScannerReportReader reader; | |||||
private BranchConfiguration branchConfiguration; | private BranchConfiguration branchConfiguration; | ||||
@Before | @Before | ||||
branchConfiguration = mock(BranchConfiguration.class); | branchConfiguration = mock(BranchConfiguration.class); | ||||
outputDir = temp.newFolder(); | outputDir = temp.newFolder(); | ||||
writer = new ScannerReportWriter(outputDir); | writer = new ScannerReportWriter(outputDir); | ||||
reader = new ScannerReportReader(outputDir); | |||||
} | |||||
private void writeIssue(int componentId) { | |||||
writer.writeComponentIssues(componentId, Collections.singleton(ScannerReport.Issue.newBuilder().build())); | |||||
} | } | ||||
@Test | @Test | ||||
assertThat(reader.readComponent(7).getStatus()).isEqualTo(FileStatus.ADDED); | assertThat(reader.readComponent(7).getStatus()).isEqualTo(FileStatus.ADDED); | ||||
} | } | ||||
@Test | |||||
public void should_set_modified_name_with_branch() throws IOException { | |||||
ProjectInfo projectInfo = mock(ProjectInfo.class); | |||||
when(projectInfo.getAnalysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); | |||||
ProjectDefinition rootDef = ProjectDefinition.create() | |||||
.setKey("foo") | |||||
.setDescription("Root description") | |||||
.setBaseDir(temp.newFolder()) | |||||
.setWorkDir(temp.newFolder()) | |||||
.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "my_branch"); | |||||
DefaultInputProject project = new DefaultInputProject(rootDef, 1); | |||||
InputComponentStore store = new InputComponentStore(branchConfiguration); | |||||
ComponentsPublisher publisher = new ComponentsPublisher(project, store); | |||||
publisher.publish(writer); | |||||
Component rootProtobuf = reader.readComponent(1); | |||||
assertThat(rootProtobuf.getKey()).isEqualTo("foo"); | |||||
assertThat(rootProtobuf.getName()).isEqualTo("foo my_branch"); | |||||
} | |||||
@Test | @Test | ||||
public void publish_unchanged_components_even_in_short_branches() throws IOException { | public void publish_unchanged_components_even_in_short_branches() throws IOException { | ||||
when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); | when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); | ||||
} | } | ||||
@Test | @Test | ||||
public void publish_project_with_links_and_branch() throws Exception { | |||||
public void publish_project_with_links() throws Exception { | |||||
ProjectInfo projectInfo = mock(ProjectInfo.class); | ProjectInfo projectInfo = mock(ProjectInfo.class); | ||||
when(projectInfo.getAnalysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); | when(projectInfo.getAnalysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); | ||||
ProjectDefinition rootDef = ProjectDefinition.create() | ProjectDefinition rootDef = ProjectDefinition.create() | ||||
.setKey("foo") | .setKey("foo") | ||||
.setProperty(CoreProperties.PROJECT_VERSION_PROPERTY, "1.0") | .setProperty(CoreProperties.PROJECT_VERSION_PROPERTY, "1.0") | ||||
.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "my_branch") | |||||
.setName("Root project") | .setName("Root project") | ||||
.setProperty(CoreProperties.LINKS_HOME_PAGE, "http://home") | .setProperty(CoreProperties.LINKS_HOME_PAGE, "http://home") | ||||
.setProperty(CoreProperties.LINKS_CI, "http://ci") | .setProperty(CoreProperties.LINKS_CI, "http://ci") |
import org.junit.Test; | import org.junit.Test; | ||||
import org.junit.rules.TemporaryFolder; | import org.junit.rules.TemporaryFolder; | ||||
import org.junit.runner.RunWith; | import org.junit.runner.RunWith; | ||||
import org.sonar.api.CoreProperties; | |||||
import org.sonar.api.batch.bootstrap.ProjectDefinition; | import org.sonar.api.batch.bootstrap.ProjectDefinition; | ||||
import org.sonar.api.batch.scm.ScmProvider; | import org.sonar.api.batch.scm.ScmProvider; | ||||
import org.sonar.scanner.ProjectInfo; | import org.sonar.scanner.ProjectInfo; | ||||
import org.sonar.scanner.scm.ScmConfiguration; | import org.sonar.scanner.scm.ScmConfiguration; | ||||
import org.sonar.scanner.scm.ScmRevision; | import org.sonar.scanner.scm.ScmRevision; | ||||
import static java.util.Arrays.asList; | |||||
import static java.util.Collections.emptyMap; | import static java.util.Collections.emptyMap; | ||||
import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; | import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; | ||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
@Test | @Test | ||||
public void write_metadata() throws Exception { | public void write_metadata() throws Exception { | ||||
Date date = new Date(); | Date date = new Date(); | ||||
when(qProfiles.findAll()).thenReturn(asList(new QProfile("q1", "Q1", "java", date))); | |||||
when(qProfiles.findAll()).thenReturn(Collections.singletonList(new QProfile("q1", "Q1", "java", date))); | |||||
when(pluginRepository.getPluginsByKey()).thenReturn(ImmutableMap.of( | when(pluginRepository.getPluginsByKey()).thenReturn(ImmutableMap.of( | ||||
"java", new ScannerPlugin("java", 12345L, null), | "java", new ScannerPlugin("java", 12345L, null), | ||||
"php", new ScannerPlugin("php", 45678L, null))); | "php", new ScannerPlugin("php", 45678L, null))); | ||||
.build())); | .build())); | ||||
} | } | ||||
@Test | |||||
public void write_project_branch() throws Exception { | |||||
when(cpdSettings.isCrossProjectDuplicationEnabled()).thenReturn(false); | |||||
ProjectDefinition projectDef = ProjectDefinition.create() | |||||
.setKey("foo") | |||||
.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch"); | |||||
createPublisher(projectDef); | |||||
File outputDir = temp.newFolder(); | |||||
ScannerReportWriter writer = new ScannerReportWriter(outputDir); | |||||
underTest.publish(writer); | |||||
ScannerReportReader reader = new ScannerReportReader(outputDir); | |||||
ScannerReport.Metadata metadata = reader.readMetadata(); | |||||
assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L); | |||||
assertThat(metadata.getProjectKey()).isEqualTo("root"); | |||||
assertThat(metadata.getDeprecatedBranch()).isEqualTo("myBranch"); | |||||
assertThat(metadata.getCrossProjectDuplicationActivated()).isFalse(); | |||||
} | |||||
@Test | @Test | ||||
public void write_project_organization() throws Exception { | public void write_project_organization() throws Exception { | ||||
when(properties.organizationKey()).thenReturn(Optional.of("SonarSource")); | when(properties.organizationKey()).thenReturn(Optional.of("SonarSource")); |
underTest = new ProjectRepositoriesSupplier(loader, props, branchConfiguration); | underTest = new ProjectRepositoriesSupplier(loader, props, branchConfiguration); | ||||
Map<String, FileData> fileMap = Maps.newHashMap(); | Map<String, FileData> fileMap = Maps.newHashMap(); | ||||
project = new SingleProjectRepository(fileMap); | project = new SingleProjectRepository(fileMap); | ||||
when(props.getKeyWithBranch()).thenReturn("key"); | |||||
when(props.getProjectKey()).thenReturn("key"); | |||||
} | } | ||||
@Test | @Test | ||||
assertThat(repo.exists()).isEqualTo(true); | assertThat(repo.exists()).isEqualTo(true); | ||||
verify(props).getKeyWithBranch(); | |||||
verify(props).getProjectKey(); | |||||
verify(loader).load(eq("key"), eq(null)); | verify(loader).load(eq("key"), eq(null)); | ||||
verifyNoMoreInteractions(loader, props); | verifyNoMoreInteractions(loader, props); | ||||
} | } |
public void setUp() { | public void setUp() { | ||||
qualityProfileProvider = new QualityProfilesProvider(); | qualityProfileProvider = new QualityProfilesProvider(); | ||||
when(props.getKeyWithBranch()).thenReturn("project"); | |||||
when(props.getProjectKey()).thenReturn("project"); | |||||
response = new ArrayList<>(1); | response = new ArrayList<>(1); | ||||
response.add(QualityProfile.newBuilder().setKey("profile").setName("profile").setLanguage("lang").setRulesUpdatedAt(DateUtils.formatDateTime(new Date())).build()); | response.add(QualityProfile.newBuilder().setKey("profile").setName("profile").setLanguage("lang").setRulesUpdatedAt(DateUtils.formatDateTime(new Date())).build()); |
out.close(); | out.close(); | ||||
when(response.contentStream()).thenReturn(in); | when(response.contentStream()).thenReturn(in); | ||||
when(wsClient.call(any())).thenReturn(response); | when(wsClient.call(any())).thenReturn(response); | ||||
when(properties.getKeyWithBranch()).thenReturn("project_key"); | |||||
when(properties.getProjectKey()).thenReturn("project_key"); | |||||
Map<String, String> result = underTest.loadProjectSettings(); | Map<String, String> result = underTest.loadProjectSettings(); | ||||
public class ModuleIndexerTest { | public class ModuleIndexerTest { | ||||
private ModuleIndexer indexer; | private ModuleIndexer indexer; | ||||
private DefaultInputModuleHierarchy moduleHierarchy; | private DefaultInputModuleHierarchy moduleHierarchy; | ||||
private InputComponentStore componentStore; | |||||
public void createIndexer() { | public void createIndexer() { | ||||
componentStore = new InputComponentStore(mock(BranchConfiguration.class)); | |||||
InputComponentStore componentStore = new InputComponentStore(mock(BranchConfiguration.class)); | |||||
moduleHierarchy = mock(DefaultInputModuleHierarchy.class); | moduleHierarchy = mock(DefaultInputModuleHierarchy.class); | ||||
indexer = new ModuleIndexer(componentStore, moduleHierarchy); | indexer = new ModuleIndexer(componentStore, moduleHierarchy); | ||||
} | } | ||||
when(mod2.key()).thenReturn("mod2"); | when(mod2.key()).thenReturn("mod2"); | ||||
when(mod3.key()).thenReturn("mod3"); | when(mod3.key()).thenReturn("mod3"); | ||||
when(root.getKeyWithBranch()).thenReturn("root"); | |||||
when(mod1.getKeyWithBranch()).thenReturn("mod1"); | |||||
when(mod2.getKeyWithBranch()).thenReturn("mod2"); | |||||
when(mod3.getKeyWithBranch()).thenReturn("mod3"); | |||||
when(root.definition()).thenReturn(rootDef); | when(root.definition()).thenReturn(rootDef); | ||||
when(mod1.definition()).thenReturn(def); | when(mod1.definition()).thenReturn(def); | ||||
when(mod2.definition()).thenReturn(def); | when(mod2.definition()).thenReturn(def); |
underTest.validate(reactor); | underTest.validate(reactor); | ||||
} | } | ||||
@Test | |||||
@UseDataProvider("validBranches") | |||||
public void not_fail_with_valid_branch(String validBranch) { | |||||
ProjectReactor reactor = createProjectReactor("foo", validBranch); | |||||
underTest.validate(reactor); | |||||
} | |||||
@DataProvider | |||||
public static Object[][] validBranches() { | |||||
return new Object[][] { | |||||
{"branch"}, | |||||
{"Branch2"}, | |||||
{"bra.nch"}, | |||||
{"bra-nch"}, | |||||
{"1"}, | |||||
{"bra_nch"} | |||||
}; | |||||
} | |||||
@Test | |||||
@UseDataProvider("invalidBranches") | |||||
public void fail_with_invalid_branch(String invalidBranch) { | |||||
ProjectReactor reactor = createProjectReactor("foo", invalidBranch); | |||||
thrown.expect(MessageException.class); | |||||
thrown.expectMessage("\"" + invalidBranch + "\" is not a valid branch name"); | |||||
underTest.validate(reactor); | |||||
} | |||||
@DataProvider | |||||
public static Object[][] invalidBranches() { | |||||
return new Object[][] { | |||||
{"bran#ch"}, | |||||
{"bran:ch"} | |||||
}; | |||||
} | |||||
@Test | @Test | ||||
public void fail_when_branch_name_is_specified_but_branch_plugin_not_present() { | public void fail_when_branch_name_is_specified_but_branch_plugin_not_present() { | ||||
ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, "foo"); | ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, "foo"); | ||||
}; | }; | ||||
} | } | ||||
private ProjectReactor createProjectReactor(String projectKey, String branch) { | |||||
return createProjectReactor(projectKey, def -> def | |||||
.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, branch)); | |||||
} | |||||
private ProjectReactor createProjectReactor(String projectKey, Consumer<ProjectDefinition>... consumers) { | private ProjectReactor createProjectReactor(String projectKey, Consumer<ProjectDefinition>... consumers) { | ||||
ProjectDefinition def = ProjectDefinition.create() | ProjectDefinition def = ProjectDefinition.create() | ||||
.setProperty(CoreProperties.PROJECT_KEY_PROPERTY, projectKey); | .setProperty(CoreProperties.PROJECT_KEY_PROPERTY, projectKey); |
import org.junit.Before; | import org.junit.Before; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.sonar.scanner.bootstrap.ProcessedScannerProperties; | |||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.mockito.ArgumentMatchers.anyString; | |||||
import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||
import static org.mockito.Mockito.when; | import static org.mockito.Mockito.when; | ||||
private ProjectBranchesProvider provider = new ProjectBranchesProvider(); | private ProjectBranchesProvider provider = new ProjectBranchesProvider(); | ||||
private ProjectBranchesLoader mockLoader; | private ProjectBranchesLoader mockLoader; | ||||
private ProjectBranches mockBranches; | private ProjectBranches mockBranches; | ||||
private ProcessedScannerProperties scannerProperties; | |||||
@Before | @Before | ||||
public void setUp() { | public void setUp() { | ||||
mockLoader = mock(ProjectBranchesLoader.class); | mockLoader = mock(ProjectBranchesLoader.class); | ||||
mockBranches = mock(ProjectBranches.class); | mockBranches = mock(ProjectBranches.class); | ||||
scannerProperties = mock(ProcessedScannerProperties.class); | |||||
} | } | ||||
@Test | @Test | ||||
public void should_cache_branches() { | public void should_cache_branches() { | ||||
ProjectBranches branches = provider.provide(null, () -> "project"); | |||||
assertThat(provider.provide(null, () -> "project")).isSameAs(branches); | |||||
when(scannerProperties.getProjectKey()).thenReturn("project"); | |||||
ProjectBranches branches = provider.provide(null, scannerProperties); | |||||
assertThat(provider.provide(null, scannerProperties)).isSameAs(branches); | |||||
} | } | ||||
@Test | @Test | ||||
public void should_use_loader() { | public void should_use_loader() { | ||||
when(scannerProperties.getProjectKey()).thenReturn("key"); | |||||
when(mockLoader.load("key")).thenReturn(mockBranches); | when(mockLoader.load("key")).thenReturn(mockBranches); | ||||
ProjectBranches branches = provider.provide(mockLoader, () -> "key"); | |||||
ProjectBranches branches = provider.provide(mockLoader, scannerProperties); | |||||
assertThat(branches).isSameAs(mockBranches); | assertThat(branches).isSameAs(mockBranches); | ||||
} | } | ||||
@Test | @Test | ||||
public void should_return_default_if_no_loader() { | public void should_return_default_if_no_loader() { | ||||
ProjectBranches branches = provider.provide(null, () -> "project"); | |||||
ProjectBranches branches = provider.provide(null, scannerProperties); | |||||
assertThat(branches.isEmpty()).isTrue(); | assertThat(branches.isEmpty()).isTrue(); | ||||
} | } | ||||
} | } |
import org.junit.Before; | import org.junit.Before; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.sonar.scanner.bootstrap.ProcessedScannerProperties; | |||||
import static java.util.Collections.emptyList; | import static java.util.Collections.emptyList; | ||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
private ProjectPullRequestsProvider provider = new ProjectPullRequestsProvider(); | private ProjectPullRequestsProvider provider = new ProjectPullRequestsProvider(); | ||||
private ProjectPullRequestsLoader mockLoader; | private ProjectPullRequestsLoader mockLoader; | ||||
private ProjectPullRequests pullRequests; | private ProjectPullRequests pullRequests; | ||||
private ProcessedScannerProperties scannerProperties; | |||||
@Before | @Before | ||||
public void setUp() { | public void setUp() { | ||||
mockLoader = mock(ProjectPullRequestsLoader.class); | mockLoader = mock(ProjectPullRequestsLoader.class); | ||||
pullRequests = new ProjectPullRequests(emptyList()); | pullRequests = new ProjectPullRequests(emptyList()); | ||||
scannerProperties = mock(ProcessedScannerProperties.class); | |||||
} | } | ||||
@Test | @Test | ||||
public void cache_pull_requests() { | public void cache_pull_requests() { | ||||
ProjectPullRequests pullRequests = provider.provide(null, () -> "project"); | |||||
when(scannerProperties.getProjectKey()).thenReturn("project"); | |||||
ProjectPullRequests pullRequests = provider.provide(null, scannerProperties); | |||||
assertThat(provider.provide(null, () -> "project")).isSameAs(pullRequests); | |||||
assertThat(provider.provide(null, scannerProperties)).isSameAs(pullRequests); | |||||
} | } | ||||
@Test | @Test | ||||
public void should_use_loader() { | public void should_use_loader() { | ||||
when(scannerProperties.getProjectKey()).thenReturn("key"); | |||||
when(mockLoader.load("key")).thenReturn(pullRequests); | when(mockLoader.load("key")).thenReturn(pullRequests); | ||||
ProjectPullRequests result = provider.provide(mockLoader, () -> "key"); | |||||
ProjectPullRequests result = provider.provide(mockLoader, scannerProperties); | |||||
assertThat(result).isSameAs(pullRequests); | assertThat(result).isSameAs(pullRequests); | ||||
} | } | ||||
@Test | @Test | ||||
public void should_return_default_if_no_loader() { | public void should_return_default_if_no_loader() { | ||||
ProjectPullRequests result = provider.provide(null, () -> "project"); | |||||
when(scannerProperties.getProjectKey()).thenReturn("project"); | |||||
ProjectPullRequests result = provider.provide(null, scannerProperties); | |||||
assertThat(result.isEmpty()).isTrue(); | assertThat(result.isEmpty()).isTrue(); | ||||
} | } |
string organization_key = 2; | string organization_key = 2; | ||||
// TODO should we keep this project_key here or not ? Because it's a duplication of Component.key | // TODO should we keep this project_key here or not ? Because it's a duplication of Component.key | ||||
string project_key = 3; | string project_key = 3; | ||||
// maps the property sonar.branch | |||||
string deprecated_branch = 4; | |||||
reserved 4; // deprecated_branch (legacy branches feature) | |||||
int32 root_component_ref = 5; | int32 root_component_ref = 5; | ||||
bool cross_project_duplication_activated = 6; | bool cross_project_duplication_activated = 6; | ||||
map<string, QProfile> qprofiles_per_language = 7; | map<string, QProfile> qprofiles_per_language = 7; | ||||
string language = 3; | string language = 3; | ||||
int64 rulesUpdatedAt = 4; | int64 rulesUpdatedAt = 4; | ||||
} | } | ||||
message Plugin { | message Plugin { | ||||
string key = 1; | string key = 1; | ||||
int64 updatedAt = 2; | int64 updatedAt = 2; | ||||
message ComponentLink { | message ComponentLink { | ||||
ComponentLinkType type = 1; | ComponentLinkType type = 1; | ||||
string href = 2; | string href = 2; | ||||
enum ComponentLinkType { | enum ComponentLinkType { | ||||
UNSET = 0; | UNSET = 0; | ||||
HOME = 1; | HOME = 1; | ||||
DoubleValue double_value = 5; | DoubleValue double_value = 5; | ||||
StringValue string_value = 6; | StringValue string_value = 6; | ||||
} | } | ||||
message BoolValue { | message BoolValue { | ||||
bool value = 1; | bool value = 1; | ||||
string data = 2; | string data = 2; | ||||
} | } | ||||
message IntValue { | message IntValue { | ||||
int32 value = 1; | int32 value = 1; | ||||
string data = 2; | string data = 2; | ||||
} | } | ||||
message LongValue { | message LongValue { | ||||
int64 value = 1; | int64 value = 1; | ||||
string data = 2; | string data = 2; | ||||
} | } | ||||
message DoubleValue { | message DoubleValue { | ||||
double value = 1; | double value = 1; | ||||
string data = 2; | string data = 2; | ||||
} | } | ||||
message StringValue { | message StringValue { | ||||
string value = 1; | string value = 1; | ||||
} | } | ||||
message SyntaxHighlightingRule { | message SyntaxHighlightingRule { | ||||
TextRange range = 1; | TextRange range = 1; | ||||
HighlightingType type = 2; | HighlightingType type = 2; | ||||
enum HighlightingType { | enum HighlightingType { | ||||
UNSET = 0; | UNSET = 0; | ||||
ANNOTATION = 1; | ANNOTATION = 1; |