/* * Copyright 2015 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.config; import com.thoughtworks.go.config.validation.FilePathTypeValidator; import com.thoughtworks.go.domain.*; import com.thoughtworks.go.util.FileUtil; import com.thoughtworks.go.util.StringUtil; import com.thoughtworks.go.work.GoPublisher; import org.apache.commons.lang.StringUtils; import java.io.File; import java.text.MessageFormat; import java.util.List; import static com.thoughtworks.go.util.ExceptionUtils.bomb; import static com.thoughtworks.go.util.FileUtil.normalizePath; import static com.thoughtworks.go.util.FileUtil.subtractPath; import static com.thoughtworks.go.util.SelectorUtils.rtrimStandardrizedWildcardTokens; import static org.apache.commons.lang.StringUtils.removeStart; @ConfigTag("artifact") public class ArtifactPlan extends PersistentObject implements Artifact { public static final File DEFAULT_ROOT = new File(""); public static final String SRC = "src"; public static final String DEST = "dest"; @ConfigAttribute(value = "src", optional = false) private String src; @ConfigAttribute("dest") private String dest = DEFAULT_ROOT.getPath(); private ArtifactType artifactType = ArtifactType.file; private long buildId; private ConfigErrors errors = new ConfigErrors(); public static final String ARTIFACT_PLAN_DISPLAY_NAME = "Build Artifact"; public ArtifactPlan() { } protected ArtifactPlan(ArtifactType artifactType, String source) { setSrc(source); this.artifactType = artifactType; } protected ArtifactPlan(ArtifactType artifactType, String source, String destination) { setSrc(source); setDest(destination); this.artifactType = artifactType; } public ArtifactPlan(String source, String destination) { setSrc(source); setDest(destination); } public ArtifactPlan(ArtifactPlan other) { this(other.artifactType, other.src, other.dest); this.errors = other.errors; } public File getSource(File rootPath) { return new File(FileUtil.applyBaseDirIfRelativeAndNormalize(rootPath, new File(getSrc()))); } public String getDest() { return normalizePath(dest); } public void setBuildId(long buildInstanceId) { this.buildId = buildInstanceId; } public void setSrc(String src) { this.src = StringUtils.trim(src); } public String getSrc() { return normalizePath(src); } public void setDest(String dest) { this.dest = StringUtils.trim(dest); } public boolean equals(Object other) { return this == other || other != null && other instanceof ArtifactPlan && equals((ArtifactPlan) other); } private boolean equals(ArtifactPlan other) { if (dest != null ? !dest.equals(other.dest) : other.dest != null) { return false; } return !(src != null ? !src.equals(other.src) : other.src != null) && artifactType.equals(other.artifactType); } public int hashCode() { int result = 0; result = 31 * result + (src != null ? src.hashCode() : 0); result = 31 * result + (dest != null ? dest.hashCode() : 0); result = 31 * result + (artifactType != null ? artifactType.hashCode() : 0); return result; } public void publish(GoPublisher publisher, final File rootPath) { WildcardScanner scanner = getArtifactSrc(rootPath); File[] files = scanner.getFiles(); if (files.length == 0) { String message = "The rule [" + getSrc() + "] cannot match any resource under [" + rootPath + "]"; publisher.taggedConsumeLineWithPrefix(GoPublisher.PUBLISH_ERR, message); throw new RuntimeException(message); } for (File file : files) { publishWithProperties(publisher, file, rootPath); } } private WildcardScanner getArtifactSrc(File rootPath) { return new WildcardScanner(rootPath, getSrc()); } protected void publishWithProperties(GoPublisher goPublisher, File fileToUpload, File rootPath) { goPublisher.upload(fileToUpload, destURL(rootPath, fileToUpload)); } public String toString() { return MessageFormat.format("Artifact of type {0} copies from {1} to {2}", artifactType, src, dest); } public ArtifactType getArtifactType() { return artifactType; } protected void setArtifactType(ArtifactType artifactType) { this.artifactType = artifactType; } public String getArtifactTypeValue() { return ARTIFACT_PLAN_DISPLAY_NAME; } public String destURL(File rootPath, File file) { return destURL(rootPath, file, getSrc(), getDest()); } protected String destURL(File rootPath, File file, String src, String dest) { String trimmedPattern = rtrimStandardrizedWildcardTokens(src); if (StringUtils.equals(normalizePath(trimmedPattern), normalizePath(src))) { return dest; } String trimmedPath = removeStart(subtractPath(rootPath, file), normalizePath(trimmedPattern)); if (!StringUtils.startsWith(trimmedPath, "/") && StringUtils.isNotEmpty(trimmedPath)) { trimmedPath = "/" + trimmedPath; } return dest + trimmedPath; } public void printSrc(StringBuilder builder) { builder.append('[').append(getSrc()).append(']'); } public String effectiveDestinationPath() { File src = new File(getSrc()); return normalizePath(StringUtils.isEmpty(getDest()) ? src.getName() : new File(getDest(), src.getName()).getPath()); } public boolean validateTree(ValidationContext validationContext) { validate(validationContext); return errors().isEmpty(); } public void validate(ValidationContext validationContext) { if (!StringUtil.isBlank(dest) && (!(dest.equals(DEFAULT_ROOT.getPath()) || new FilePathTypeValidator().isPathValid(dest)))) { addError(DEST, "Invalid destination path. Destination path should match the pattern " + FilePathTypeValidator.PATH_PATTERN); } if (StringUtil.isBlank(src)) { addError(SRC, String.format("Job '%s' has an artifact with an empty source", validationContext.getJob().name())); } } public ConfigErrors errors() { return errors; } public void addError(String fieldName, String message) { errors.add(fieldName, message); } public void validateUniqueness(List<ArtifactPlan> existingPlans) { for (ArtifactPlan existingPlan : existingPlans) { if (this.equals(existingPlan)) { this.addUniquenessViolationError(); existingPlan.addUniquenessViolationError(); return; } } existingPlans.add(this); } private void addUniquenessViolationError() { addError(SRC, "Duplicate artifacts defined."); addError(DEST, "Duplicate artifacts defined."); } public static ArtifactPlan create(ArtifactType artifactType, String src, String dest) { if (artifactType == ArtifactType.file) { return new ArtifactPlan(src, dest); } else if (artifactType == ArtifactType.unit) { return new TestArtifactPlan(src, dest); } else { throw bomb("ArtifactType not specified"); } } }