
hudson.tasks.Fingerprinter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hudson-core Show documentation
Show all versions of hudson-core Show documentation
Contains the core Hudson code and view files to render HTML.
The newest version!
/*******************************************************************************
*
* Copyright (c) 2004-2010 Oracle Corporation.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*
* Kohsuke Kawaguchi
*
*
*******************************************************************************/
package hudson.tasks;
import com.google.common.collect.ImmutableMap;
import hudson.Extension;
import hudson.FilePath;
import hudson.FilePath.FileCallable;
import hudson.Launcher;
import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Build;
import hudson.model.BuildListener;
import hudson.model.Fingerprint;
import hudson.model.Fingerprint.BuildPtr;
import hudson.model.FingerprintMap;
import hudson.model.Hudson;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.RunAction;
import hudson.remoting.VirtualChannel;
import hudson.util.FormValidation;
import hudson.util.IOException2;
import hudson.util.PackedMap;
import net.sf.json.JSONObject;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.types.FileSet;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Records fingerprints of the specified files.
*
* @author Kohsuke Kawaguchi
*/
public class Fingerprinter extends Recorder implements Serializable {
/**
* Comma-separated list of files/directories to be fingerprinted.
*/
private final String targets;
/**
* Also record all the finger prints of the build artifacts.
*/
private final boolean recordBuildArtifacts;
@DataBoundConstructor
public Fingerprinter(String targets, boolean recordBuildArtifacts) {
this.targets = targets;
this.recordBuildArtifacts = recordBuildArtifacts;
}
public String getTargets() {
return targets;
}
public boolean getRecordBuildArtifacts() {
return recordBuildArtifacts;
}
@Override
public boolean perform(AbstractBuild, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException {
try {
listener.getLogger().println(Messages.Fingerprinter_Recording());
Map record = new HashMap();
if (targets.length() != 0) {
record(build, listener, record, targets);
}
if (recordBuildArtifacts) {
ArtifactArchiver aa = build.getProject().getPublishersList().get(ArtifactArchiver.class);
if (aa == null) {
// configuration error
listener.error(Messages.Fingerprinter_NoArchiving());
build.setResult(Result.FAILURE);
return true;
}
record(build, listener, record, aa.getArtifacts());
}
FingerprintAction.add(build, record);
} catch (IOException e) {
e.printStackTrace(listener.error(Messages.Fingerprinter_Failed()));
build.setResult(Result.FAILURE);
}
// failing to record fingerprints is an error but not fatal
return true;
}
public BuildStepMonitor getRequiredMonitorService() {
return BuildStepMonitor.NONE;
}
private void record(AbstractBuild, ?> build, BuildListener listener, Map record, final String targets) throws IOException, InterruptedException {
final class Record implements Serializable {
final boolean produced;
final String relativePath;
final String fileName;
final String md5sum;
public Record(boolean produced, String relativePath, String fileName, String md5sum) {
this.produced = produced;
this.relativePath = relativePath;
this.fileName = fileName;
this.md5sum = md5sum;
}
Fingerprint addRecord(AbstractBuild build) throws IOException {
FingerprintMap map = Hudson.getInstance().getFingerprintMap();
return map.getOrCreate(produced ? build : null, fileName, md5sum);
}
private static final long serialVersionUID = 1L;
}
final long buildTimestamp = build.getTimeInMillis();
FilePath ws = build.getWorkspace();
if (ws == null) {
listener.error(Messages.Fingerprinter_NoWorkspace());
build.setResult(Result.FAILURE);
return;
}
List records = ws.act(new FileCallable>() {
public List invoke(File baseDir, VirtualChannel channel) throws IOException {
List results = new ArrayList();
FileSet src = Util.createFileSet(baseDir, targets);
DirectoryScanner ds = src.getDirectoryScanner();
for (String f : ds.getIncludedFiles()) {
File file = new File(baseDir, f);
// consider the file to be produced by this build only if the timestamp
// is newer than when the build has started.
// 2000ms is an error margin since since VFAT only retains timestamp at 2sec precision
boolean produced = buildTimestamp <= file.lastModified() + 2000;
try {
results.add(new Record(produced, f, file.getName(), new FilePath(file).digest()));
} catch (IOException e) {
throw new IOException2(Messages.Fingerprinter_DigestFailed(file), e);
} catch (InterruptedException e) {
throw new IOException2(Messages.Fingerprinter_Aborted(), e);
}
}
return results;
}
});
for (Record r : records) {
Fingerprint fp = r.addRecord(build);
if (fp == null) {
listener.error(Messages.Fingerprinter_FailedFor(r.relativePath));
continue;
}
fp.add(build);
record.put(r.relativePath, fp.getHashString());
}
}
@Extension
public static class DescriptorImpl extends BuildStepDescriptor {
public String getDisplayName() {
return Messages.Fingerprinter_DisplayName();
}
@Override
public String getHelpFile() {
return "/help/project-config/fingerprint.html";
}
/**
* Performs on-the-fly validation on the file mask wildcard.
*/
public FormValidation doCheck(@AncestorInPath AbstractProject project, @QueryParameter String value) throws IOException {
return FilePath.validateFileMask(project.getSomeWorkspace(), value);
}
@Override
public Publisher newInstance(StaplerRequest req, JSONObject formData) {
return req.bindJSON(Fingerprinter.class, formData);
}
public boolean isApplicable(Class extends AbstractProject> jobType) {
return true;
}
}
/**
* Action for displaying fingerprints.
*
* To ensure there is only one per build use
* {@link FingerprintAction#add(AbstractBuild, Map)}. This allows for
* additional fingerprint contributions outside of the
* {@link Fingerprinter}.
*/
public static final class FingerprintAction implements RunAction {
private final AbstractBuild build;
/**
* From file name to the digest.
*/
private /*almost final*/ Map record;
private transient WeakReference
© 2015 - 2025 Weber Informatics LLC | Privacy Policy