/* * Copyright 2016 ThoughtWorks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.thoughtworks.go.domain.buildcause; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; import com.thoughtworks.go.config.CaseInsensitiveString; import com.thoughtworks.go.config.EnvironmentVariablesConfig; import com.thoughtworks.go.config.PipelineConfig; import com.thoughtworks.go.config.materials.MaterialConfigs; import com.thoughtworks.go.config.materials.Materials; import com.thoughtworks.go.config.remote.RepoConfigOrigin; import com.thoughtworks.go.domain.MaterialRevision; import com.thoughtworks.go.domain.MaterialRevisions; import com.thoughtworks.go.domain.ModificationSummaries; import com.thoughtworks.go.domain.ModificationVisitorAdapter; import com.thoughtworks.go.domain.materials.Material; import com.thoughtworks.go.domain.materials.MaterialConfig; import com.thoughtworks.go.domain.materials.Revision; import com.thoughtworks.go.server.domain.Username; /** * @understands why a pipeline was triggered and what revisions it contains */ public class BuildCause implements Serializable { private MaterialRevisions materialRevisions = MaterialRevisions.EMPTY; private BuildTrigger trigger; private static final BuildCause NEVER_RUN = new BuildCause(MaterialRevisions.EMPTY, BuildTrigger.forNeverRun(), ""); private String approver; private EnvironmentVariablesConfig variables; protected BuildCause() { variables = new EnvironmentVariablesConfig(); } private BuildCause(MaterialRevisions materialRevisions, BuildTrigger trigger, String approver) { this(); this.approver = approver; if (materialRevisions != null) { this.materialRevisions = materialRevisions; } this.trigger = trigger; } public MaterialRevisions getMaterialRevisions() { return materialRevisions; } public final String getBuildCauseMessage() { return trigger.getMessage(); } public void setMessage(String buildCauseMessage) { trigger.setMessage(buildCauseMessage); } public final boolean isForced() { return trigger.isForced(); } public void setMaterialRevisions(MaterialRevisions materialRevisions) { this.materialRevisions = materialRevisions; BuildTrigger newTrigger = BuildTrigger.forModifications(materialRevisions); if (newTrigger.trumps(this.trigger)) { this.trigger = newTrigger; } } public ModificationSummaries toModificationSummaries() { return new ModificationSummaries(getMaterialRevisions()); } public boolean trumps(BuildCause existingBuildCause) { return trigger.trumps(existingBuildCause.trigger); } public Date getModifiedDate() { return materialRevisions.getDateOfLatestModification(); } public Materials materials() { final List<Material> materials = new ArrayList<>(); materialRevisions.accept(new ModificationVisitorAdapter() { public void visit(Material material, Revision revision) { materials.add(material); } }); return new Materials(materials); } public String toString() { return String.format("[%s: %s]", trigger.getDbName(), getBuildCauseMessage()); } public boolean materialsMatch(MaterialConfigs other){ try { assertMaterialsMatch(other); } catch (BuildCauseOutOfDateException e) { return false; } return true; } public boolean pipelineConfigAndMaterialRevisionMatch(PipelineConfig pipelineConfig){ if(!pipelineConfig.isConfigOriginSameAsOneOfMaterials()) { return true; } RepoConfigOrigin repoConfigOrigin = (RepoConfigOrigin)pipelineConfig.getOrigin(); MaterialConfig configAndCodeMaterial = repoConfigOrigin.getMaterial(); //TODO if revision in any of the pipelines match MaterialRevision revision = this.getMaterialRevisions().findRevisionForFingerPrint(configAndCodeMaterial.getFingerprint()); String revisionString = revision.getRevision().getRevision(); if(pipelineConfig.isConfigOriginFromRevision(revisionString)) { return true; } return false; } public void assertMaterialsMatch(MaterialConfigs other) { Materials materialsFromBuildCause = materials(); for (MaterialConfig materialConfig : other) { if (!materialsFromBuildCause.hasMaterialConfigWithFingerprint(materialConfig)) { invalid(other); } } } public void assertPipelineConfigAndMaterialRevisionMatch(PipelineConfig pipelineConfig) { if(!pipelineConfig.isConfigOriginSameAsOneOfMaterials()) { return; } // then config and code revision must both match if(this.trigger.isForced()) { // we should not check when manual trigger because of re-runs // and possibility to specify revisions to run with return; } RepoConfigOrigin repoConfigOrigin = (RepoConfigOrigin)pipelineConfig.getOrigin(); MaterialConfig configAndCodeMaterial = repoConfigOrigin.getMaterial(); //TODO if revision in any of the pipelines match MaterialRevision revision = this.getMaterialRevisions().findRevisionForFingerPrint(configAndCodeMaterial.getFingerprint()); String revisionString = revision.getRevision().getRevision(); if(pipelineConfig.isConfigOriginFromRevision(revisionString)) { return; } invalidRevision(repoConfigOrigin.getRevision(),revisionString); } private void invalidRevision(String configRevision,String codeRevision) { throw new BuildCauseOutOfDateException( "Illegal build cause - pipeline configuration is from different revision than scm material. " + "Pipeline configuration revision: " + configRevision + " " + "while revision to schedule is : " + codeRevision + ""); } private void invalid(MaterialConfigs materialsFromConfig) { throw new BuildCauseOutOfDateException( "Illegal build cause - it has different materials from the pipeline it is trying to trigger. " + "Materials for which modifications are available: " + materials() + " " + "while pipeline is configured with: " + materialsFromConfig + ""); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } BuildCause that = (BuildCause) o; if (materialRevisions != null ? !materialRevisions.equals(that.materialRevisions) : that.materialRevisions != null) { return false; } if (trigger != null ? !trigger.equals(that.trigger) : that.trigger != null) { return false; } return true; } @Override public int hashCode() { int result = materialRevisions != null ? materialRevisions.hashCode() : 0; result = 31 * result + (trigger != null ? trigger.hashCode() : 0); return result; } public boolean isSameAs(BuildCause buildCause) { if (this.trigger.getDbName() != buildCause.trigger.getDbName()) { return false; } return materialRevisions.isSameAs(buildCause.materialRevisions); } public boolean hasNeverRun() { return this.equals(createNeverRun()); } public static BuildCause createManualForced(MaterialRevisions materialRevisions, Username username) { if (username == null) { throw new IllegalArgumentException("Username cannot be null"); } String cause = ""; if (materialRevisions != null && !materialRevisions.isEmpty()) { cause = materialRevisions.buildCausedBy(); } String message = String.format("Forced by %s", username.getDisplayName()); return new BuildCause(materialRevisions, BuildTrigger.forForced(message), CaseInsensitiveString.str(username.getUsername())); } public static BuildCause createManualForced() { return createManualForced(MaterialRevisions.EMPTY, Username.ANONYMOUS); } public static BuildCause fromDbString(String text) { if (text.equals(BuildTrigger.FORCED_BUILD_CAUSE)) { return BuildCause.createManualForced(MaterialRevisions.EMPTY, Username.ANONYMOUS); } else if (text.equals(BuildTrigger.MODIFICATION_BUILD_CAUSE)) { return createWithEmptyModifications(); } else if (text.equals(BuildTrigger.EXTERNAL_BUILD_CAUSE)) { return createExternal(); } return createWithEmptyModifications(); } public String toDbString() { return trigger.getDbName(); } public static BuildCause createWithModifications(MaterialRevisions materialRevisions, String approver) { return new BuildCause(materialRevisions, BuildTrigger.forModifications(materialRevisions), approver); } public static BuildCause createWithEmptyModifications() { return createWithModifications(MaterialRevisions.EMPTY, ""); } public static BuildCause createExternal() { return new BuildCause(MaterialRevisions.EMPTY, BuildTrigger.forExternal(), ""); } public static BuildCause createNeverRun() { return NEVER_RUN; } public String getApprover() { return approver; } public void setApprover(String approvedBy) { approver = approvedBy; } public EnvironmentVariablesConfig getVariables() { return variables; } //for ibatis public void setVariables(EnvironmentVariablesConfig variables) { this.variables = variables; } public void addOverriddenVariables(EnvironmentVariablesConfig variables) { this.variables.addAll(variables); } }