boolean isMain();
+ /**
+ * Whether branch has been created through the legacy configuration
+ * (scanner parameter sonar.branch) or not
+ */
+ boolean isLegacyFeature();
+
/**
* Name can be empty when it's not known on the main branch
* (regular analysis without the branch parameters)
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.server.computation.task.projectanalysis.api.posttask;
+
+import java.util.Optional;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.ce.posttask.Branch;
+
+@Immutable
+public class BranchImpl implements Branch {
+ private final boolean isMain;
+ @Nullable
+ private final String name;
+ private final Type type;
+
+ public BranchImpl(boolean isMain, @Nullable String name, Type type) {
+ this.isMain = isMain;
+ this.name = name;
+ this.type = type;
+ }
+
+ @Override
+ public boolean isMain() {
+ return isMain;
+ }
+
+ @Override
+ public Optional<String> getName() {
+ return Optional.ofNullable(name);
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("Branch{");
+ sb.append("isMain=").append(isMain);
+ sb.append(", name='").append(name).append('\'');
+ sb.append(", type=").append(type);
+ sb.append('}');
+ return sb.toString();
+ }
+}
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import org.sonar.api.ce.posttask.Branch;
import org.sonar.api.ce.posttask.CeTask;
import org.sonar.api.ce.posttask.PostProjectAnalysisTask;
import org.sonar.api.ce.posttask.Project;
private ProjectAnalysis createProjectAnalysis(CeTask.Status status) {
Long analysisDate = getAnalysisDate();
+
return new ProjectAnalysis(
new CeTaskImpl(this.ceTask.getUuid(), status),
createProject(this.ceTask),
analysisDate,
analysisDate == null ? system2.now() : analysisDate,
ScannerContextImpl.from(reportReader.readContextProperties()),
- status == SUCCESS ? createQualityGate(this.qualityGateHolder) : null);
+ status == SUCCESS ? createQualityGate() : null,
+ createBranch());
}
private static Project createProject(org.sonar.ce.queue.CeTask ceTask) {
}
@CheckForNull
- private QualityGateImpl createQualityGate(QualityGateHolder qualityGateHolder) {
- Optional<org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGate> qualityGateOptional = qualityGateHolder.getQualityGate();
+ private QualityGateImpl createQualityGate() {
+ Optional<org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGate> qualityGateOptional = this.qualityGateHolder.getQualityGate();
if (qualityGateOptional.isPresent()) {
org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGate qualityGate = qualityGateOptional.get();
return null;
}
+ @CheckForNull
+ private BranchImpl createBranch() {
+ java.util.Optional<org.sonar.server.computation.task.projectanalysis.analysis.Branch> analysisBranchOpt = analysisMetadataHolder.getBranch();
+ if (analysisBranchOpt.isPresent() && !analysisBranchOpt.get().isLegacyFeature()) {
+ org.sonar.server.computation.task.projectanalysis.analysis.Branch branch = analysisBranchOpt.get();
+ return new BranchImpl(branch.isMain(), branch.getName().orElse(null), Branch.Type.valueOf(branch.getType().name()));
+ }
+ return null;
+ }
+
private static QualityGate.Status convert(QualityGateStatus status) {
switch (status) {
case OK:
private static class ProjectAnalysis implements PostProjectAnalysisTask.ProjectAnalysis {
private final CeTask ceTask;
private final Project project;
- @CheckForNull
+ @Nullable
private final Long analysisDate;
private final long date;
private final ScannerContext scannerContext;
- @CheckForNull
+ @Nullable
private final QualityGate qualityGate;
+ @Nullable
+ private final Branch branch;
private ProjectAnalysis(CeTask ceTask, Project project,
@Nullable Long analysisDate, long date,
- ScannerContext scannerContext, @Nullable QualityGate qualityGate) {
+ ScannerContext scannerContext, @Nullable QualityGate qualityGate, @Nullable Branch branch) {
this.ceTask = requireNonNull(ceTask, "ceTask can not be null");
this.project = requireNonNull(project, "project can not be null");
this.analysisDate = analysisDate;
this.date = date;
this.scannerContext = requireNonNull(scannerContext, "scannerContext can not be null");
this.qualityGate = qualityGate;
+ this.branch = branch;
}
@Override
return project;
}
+ @Override
+ public java.util.Optional<Branch> getBranch() {
+ return java.util.Optional.ofNullable(branch);
+ }
+
@Override
@CheckForNull
public QualityGate getQualityGate() {
return true;
}
+ @Override
+ public boolean isLegacyFeature() {
+ return true;
+ }
+
@Override
public Optional<String> getName() {
return Optional.ofNullable(branchName);
import java.io.Writer;
import javax.annotation.Nullable;
import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.ce.posttask.Branch;
import org.sonar.api.ce.posttask.CeTask;
import org.sonar.api.ce.posttask.PostProjectAnalysisTask;
import org.sonar.api.ce.posttask.Project;
writeTask(writer, analysis.getCeTask());
analysis.getAnalysisDate().ifPresent(date -> writer.propDateTime("analysedAt", date));
writeProject(analysis, writer, analysis.getProject());
+ analysis.getBranch().ifPresent(b -> writeBranch(writer, b));
writeQualityGate(writer, analysis.getQualityGate());
writeAnalysisProperties(writer, analysis.getScannerContext());
writer.endObject().close();
.endObject();
}
+ private static void writeBranch(JsonWriter writer, Branch branch) {
+ writer
+ .name("branch")
+ .beginObject()
+ .prop("name", branch.getName().orElse(null))
+ .prop("type", branch.getType().name())
+ .prop("isMain", branch.isMain())
+ .endObject();
+ }
+
private static void writeQualityGate(JsonWriter writer, @Nullable QualityGate gate) {
if (gate != null) {
writer
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Date;
import java.util.List;
+import java.util.Optional;
+import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.ce.posttask.Project;
import org.sonar.api.utils.System2;
import org.sonar.ce.queue.CeTask;
+import org.sonar.db.component.BranchType;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReaderRule;
+import org.sonar.server.computation.task.projectanalysis.component.MainBranchImpl;
import org.sonar.server.computation.task.projectanalysis.metric.Metric;
import org.sonar.server.computation.task.projectanalysis.qualitygate.Condition;
import org.sonar.server.computation.task.projectanalysis.qualitygate.ConditionStatus;
qualityGateStatusHolder.setStatus(QualityGateStatus.OK, ImmutableMap.of(
CONDITION_1, ConditionStatus.create(ConditionStatus.EvaluationStatus.OK, "value"),
CONDITION_2, ConditionStatus.NO_VALUE_STATUS));
+ analysisMetadataHolder.setBranch(null);
}
@Test
@Test
public void date_comes_from_AnalysisMetadataHolder() {
- analysisMetadataHolder.setAnalysisDate(8465132498L);
+ analysisMetadataHolder.setAnalysisDate(8_465_132_498L);
underTest.finished(true);
assertThat(projectAnalysisArgumentCaptor.getValue().getAnalysisDate()).isEmpty();
}
+ @Test
+ public void branch_is_empty_when_not_set_in_AnalysisMetadataHolder() {
+ underTest.finished(false);
+
+ verify(postProjectAnalysisTask).finished(projectAnalysisArgumentCaptor.capture());
+
+ assertThat(projectAnalysisArgumentCaptor.getValue().getBranch()).isEmpty();
+ }
+
+ @Test
+ public void branch_is_empty_when_legacy_branch_implementation_is_used() {
+ analysisMetadataHolder.setBranch(new MainBranchImpl("feature/foo"));
+
+ underTest.finished(true);
+
+ verify(postProjectAnalysisTask).finished(projectAnalysisArgumentCaptor.capture());
+
+ assertThat(projectAnalysisArgumentCaptor.getValue().getBranch()).isEmpty();
+ }
+
+ @Test
+ public void branch_comes_from_AnalysisMetadataHolder_when_set() {
+ analysisMetadataHolder.setBranch(new Branch() {
+ @Override
+ public BranchType getType() {
+ return BranchType.SHORT;
+ }
+
+ @Override
+ public boolean isMain() {
+ return false;
+ }
+
+ @Override
+ public boolean isLegacyFeature() {
+ return false;
+ }
+
+ @Override
+ public Optional<String> getName() {
+ return Optional.of("feature/foo");
+ }
+
+ @Override
+ public boolean supportsCrossProjectCpd() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String generateKey(ScannerReport.Component module, @Nullable ScannerReport.Component fileOrDir) {
+ throw new UnsupportedOperationException();
+ }
+ });
+
+ underTest.finished(true);
+
+ verify(postProjectAnalysisTask).finished(projectAnalysisArgumentCaptor.capture());
+
+ org.sonar.api.ce.posttask.Branch branch = projectAnalysisArgumentCaptor.getValue().getBranch().get();
+ assertThat(branch.isMain()).isFalse();
+ assertThat(branch.getName()).hasValue("feature/foo");
+ assertThat(branch.getType()).isEqualTo(BranchImpl.Type.SHORT);
+ }
+
@Test
public void qualityGate_is_null_when_finished_method_argument_is_false() {
underTest.finished(false);
return false;
}
+ @Override
+ public boolean isLegacyFeature() {
+ return false;
+ }
+
@Override
public java.util.Optional<String> getName() {
return java.util.Optional.ofNullable(name);
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Test;
+import org.sonar.api.ce.posttask.Branch;
import org.sonar.api.ce.posttask.CeTask;
import org.sonar.api.ce.posttask.PostProjectAnalysisTask;
import org.sonar.api.ce.posttask.Project;
import org.sonar.api.ce.posttask.QualityGate;
import org.sonar.api.ce.posttask.ScannerContext;
import org.sonar.api.platform.Server;
+import org.sonar.server.computation.task.projectanalysis.api.posttask.BranchImpl;
import static java.util.Collections.emptyMap;
import static org.assertj.core.api.Assertions.assertThat;
.setErrorThreshold("70.0")
.build(QualityGate.EvaluationStatus.WARN, "74.0"))
.build();
- PostProjectAnalysisTask.ProjectAnalysis analysis = newAnalysis(task, gate, emptyMap());
+ PostProjectAnalysisTask.ProjectAnalysis analysis = newAnalysis(task, gate, null, emptyMap());
WebhookPayload payload = underTest.create(analysis);
assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
.setErrorThreshold("70.0")
.buildNoValue())
.build();
- PostProjectAnalysisTask.ProjectAnalysis analysis = newAnalysis(task, gate, emptyMap());
+ PostProjectAnalysisTask.ProjectAnalysis analysis = newAnalysis(task, gate, null, emptyMap());
WebhookPayload payload = underTest.create(analysis);
assertThat(payload.getProjectKey()).isEqualTo(PROJECT_KEY);
"sonar.analysis.revision", "ab45d24",
"sonar.analysis.buildNumber", "B123",
"not.prefixed.with.sonar.analysis", "should be ignored",
- "ignored", "should be ignored too"
- );
- PostProjectAnalysisTask.ProjectAnalysis analysis = newAnalysis(task, gate, scannerProperties);
+ "ignored", "should be ignored too");
+ PostProjectAnalysisTask.ProjectAnalysis analysis = newAnalysis(task, gate, null, scannerProperties);
WebhookPayload payload = underTest.create(analysis);
assertJson(payload.getJson()).isSimilarTo(getClass().getResource("WebhookPayloadTest/with_analysis_properties.json"));
@Test
public void create_payload_for_failed_analysis() {
CeTask ceTask = newCeTaskBuilder().setStatus(CeTask.Status.FAILED).setId("#1").build();
- PostProjectAnalysisTask.ProjectAnalysis analysis = newAnalysis(ceTask, null, emptyMap());
+ PostProjectAnalysisTask.ProjectAnalysis analysis = newAnalysis(ceTask, null, null, emptyMap());
WebhookPayload payload = underTest.create(analysis);
assertJson(payload.getJson()).isSimilarTo(getClass().getResource("WebhookPayloadTest/failed.json"));
}
- private static PostProjectAnalysisTask.ProjectAnalysis newAnalysis(CeTask task, @Nullable QualityGate gate,
- Map<String, String> scannerProperties) {
+ @Test
+ public void create_payload_on_branch() {
+ CeTask task = newCeTaskBuilder()
+ .setStatus(CeTask.Status.SUCCESS)
+ .setId("#1")
+ .build();
+ PostProjectAnalysisTask.ProjectAnalysis analysis = newAnalysis(task, null, new BranchImpl(false, "feature/foo", Branch.Type.SHORT), emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertJson(payload.getJson()).isSimilarTo("{" +
+ "\"branch\": {" +
+ " \"name\": \"feature/foo\"" +
+ " \"type\": \"SHORT\"" +
+ " \"isMain\": false" +
+ "}" +
+ "}");
+ }
+
+ @Test
+ public void create_payload_on_main_branch_without_name() {
+ CeTask task = newCeTaskBuilder()
+ .setStatus(CeTask.Status.SUCCESS)
+ .setId("#1")
+ .build();
+ PostProjectAnalysisTask.ProjectAnalysis analysis = newAnalysis(task, null, new BranchImpl(true, null, Branch.Type.LONG), emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertJson(payload.getJson()).isSimilarTo("{" +
+ "\"branch\": {" +
+ " \"type\": \"LONG\"" +
+ " \"isMain\": true" +
+ "}" +
+ "}");
+ }
+
+ private static PostProjectAnalysisTask.ProjectAnalysis newAnalysis(CeTask task, @Nullable QualityGate gate, @Nullable Branch branch,
+ Map<String, String> scannerProperties) {
return new PostProjectAnalysisTask.ProjectAnalysis() {
@Override
public CeTask getCeTask() {
.build();
}
+ @Override
+ public Optional<Branch> getBranch() {
+ return Optional.ofNullable(branch);
+ }
+
@Override
public QualityGate getQualityGate() {
return gate;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.ce.posttask;
+
+import java.util.Optional;
+
+/**
+ * @since 6.6
+ */
+public interface Branch {
+
+ enum Type {
+ LONG, SHORT
+ }
+
+ boolean isMain();
+
+ Optional<String> getName();
+
+ Type getType();
+
+}
*/
Project getProject();
+ /**
+ * The branch that is being analyzed.
+ *
+ * @since 6.6
+ */
+ Optional<Branch> getBranch();
+
/**
* Status and details of the Quality Gate of the project (if any was configured on the project).
*/
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
private Date date;
@CheckForNull
private QualityGate qualityGate;
+ @CheckForNull
+ private Branch branch;
private ScannerContext scannerContext;
private PostProjectAnalysisTaskTester(PostProjectAnalysisTask underTest) {
return new ProjectBuilder();
}
+ public static BranchBuilder newBranchBuilder() {
+ return new BranchBuilder();
+ }
+
public static QualityGateBuilder newQualityGateBuilder() {
return new QualityGateBuilder();
}
return this;
}
+ public PostProjectAnalysisTaskTester withBranch(@Nullable Branch b) {
+ this.branch = b;
+ return this;
+ }
+
public void execute() {
requireNonNull(ceTask, CE_TASK_CAN_NOT_BE_NULL);
requireNonNull(project, PROJECT_CAN_NOT_BE_NULL);
return project;
}
+ @Override
+ public Optional<Branch> getBranch() {
+ return Optional.ofNullable(branch);
+ }
+
@Override
public QualityGate getQualityGate() {
return qualityGate;
}
}
+ public static final class BranchBuilder {
+ private boolean isMain = true;
+ private String name = null;
+ private Branch.Type type = Branch.Type.LONG;
+
+ private BranchBuilder() {
+ // prevents instantiation outside PostProjectAnalysisTaskTester
+ }
+
+ public BranchBuilder setName(@Nullable String s) {
+ this.name = s;
+ return this;
+ }
+
+ public BranchBuilder setType(Branch.Type t) {
+ this.type = Objects.requireNonNull(t);
+ return this;
+ }
+
+ public BranchBuilder setIsMain(boolean b) {
+ this.isMain = b;
+ return this;
+ }
+
+ public Branch build() {
+ return new Branch() {
+
+
+ @Override
+ public boolean isMain() {
+ return isMain;
+ }
+
+ @Override
+ public Optional<String> getName() {
+ return Optional.ofNullable(name);
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+ };
+ }
+ }
+
public static final class QualityGateBuilder {
private static final String ID_CAN_NOT_BE_NULL = "id cannot be null";
private static final String NAME_CAN_NOT_BE_NULL = "name cannot be null";