/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.tasks; import hudson.AbortException; import hudson.FilePath; import hudson.Launcher; import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.BuildListener; import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; import hudson.model.Result; import static hudson.tasks.LogRotatorTest.build; import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.util.Collections; import java.util.List; import jenkins.util.VirtualFile; import org.jenkinsci.plugins.structs.describable.DescribableModel; import static org.junit.Assert.*; import static org.junit.Assume.*; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.TestBuilder; import org.jvnet.hudson.test.recipes.LocalData; public class ArtifactArchiverTest { @Rule public JenkinsRule j = new JenkinsRule(); @Test @Issue("JENKINS-3227") public void testEmptyDirectories() throws Exception { FreeStyleProject project = j.createFreeStyleProject(); Publisher artifactArchiver = new ArtifactArchiver("dir/"); project.getPublishersList().replaceBy(Collections.singleton(artifactArchiver)); project.getBuildersList().replaceBy(Collections.singleton(new TestBuilder() { public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { FilePath dir = build.getWorkspace().child("dir"); dir.child("subdir1").mkdirs(); FilePath subdir2 = dir.child("subdir2"); subdir2.mkdirs(); subdir2.child("file").write("content", "UTF-8"); return true; } })); assertEquals(Result.SUCCESS, build(project)); // #1 File artifacts = project.getBuildByNumber(1).getArtifactsDir(); File[] kids = artifacts.listFiles(); assertEquals(1, kids.length); assertEquals("dir", kids[0].getName()); kids = kids[0].listFiles(); assertEquals(1, kids.length); assertEquals("subdir2", kids[0].getName()); kids = kids[0].listFiles(); assertEquals(1, kids.length); assertEquals("file", kids[0].getName()); } @Test @Issue("JENKINS-10502") public void testAllowEmptyArchive() throws Exception { FreeStyleProject project = j.createFreeStyleProject(); ArtifactArchiver aa = new ArtifactArchiver("f"); assertFalse(aa.getAllowEmptyArchive()); aa.setAllowEmptyArchive(true); project.getPublishersList().replaceBy(Collections.singleton(aa)); assertEquals("(no artifacts)", Result.SUCCESS, build(project)); assertFalse(project.getBuildByNumber(1).getHasArtifacts()); } @Issue("JENKINS-21958") @Test public void symlinks() throws Exception { FreeStyleProject p = j.createFreeStyleProject(); p.getBuildersList().add(new TestBuilder() { @Override public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { FilePath ws = build.getWorkspace(); if (ws == null) { return false; } FilePath dir = ws.child("dir"); dir.mkdirs(); dir.child("fizz").write("contents", null); dir.child("lodge").symlinkTo("fizz", listener); return true; } }); ArtifactArchiver aa = new ArtifactArchiver("dir/lodge"); aa.setAllowEmptyArchive(true); p.getPublishersList().add(aa); FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); FilePath ws = b.getWorkspace(); assertNotNull(ws); assumeTrue("May not be testable on Windows:\n" + JenkinsRule.getLog(b), ws.child("dir/lodge").exists()); List<FreeStyleBuild.Artifact> artifacts = b.getArtifacts(); assertEquals(1, artifacts.size()); FreeStyleBuild.Artifact artifact = artifacts.get(0); assertEquals("dir/lodge", artifact.relativePath); VirtualFile[] kids = b.getArtifactManager().root().child("dir").list(); assertEquals(1, kids.length); assertEquals("lodge", kids[0].getName()); // do not check that it .exists() since its target has not been archived } @Issue("SECURITY-162") @Test public void outsideSymlinks() throws Exception { final FreeStyleProject p = j.createFreeStyleProject(); p.getBuildersList().add(new TestBuilder() { @Override public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { FilePath ws = build.getWorkspace(); if (ws == null) { return false; } ws.child("hack").symlinkTo(p.getConfigFile().getFile().getAbsolutePath(), listener); return true; } }); p.getPublishersList().add(new ArtifactArchiver("hack", "", false, true)); FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0)); List<FreeStyleBuild.Artifact> artifacts = b.getArtifacts(); assertEquals(1, artifacts.size()); FreeStyleBuild.Artifact artifact = artifacts.get(0); assertEquals("hack", artifact.relativePath); VirtualFile[] kids = b.getArtifactManager().root().list(); assertEquals(1, kids.length); assertEquals("hack", kids[0].getName()); assertFalse(kids[0].isDirectory()); assertFalse(kids[0].isFile()); assertFalse(kids[0].exists()); j.createWebClient().assertFails(b.getUrl() + "artifact/hack", HttpURLConnection.HTTP_NOT_FOUND); } static class CreateArtifact extends TestBuilder { public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException { build.getWorkspace().child("f").write("content", "UTF-8"); return true; } } static class CreateArtifactAndFail extends TestBuilder { public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException { build.getWorkspace().child("f").write("content", "UTF-8"); throw new AbortException("failing the build"); } } @Test @Issue("JENKINS-22698") public void testArchivingSkippedWhenOnlyIfSuccessfulChecked() throws Exception { FreeStyleProject project = j.createFreeStyleProject(); ArtifactArchiver aa = new ArtifactArchiver("f"); project.getPublishersList().replaceBy(Collections.singleton(aa)); project.getBuildersList().replaceBy(Collections.singleton(new CreateArtifactAndFail())); assertEquals(Result.FAILURE, build(project)); assertTrue(project.getBuildByNumber(1).getHasArtifacts()); aa.setOnlyIfSuccessful(true); assertEquals(Result.FAILURE, build(project)); assertTrue(project.getBuildByNumber(1).getHasArtifacts()); assertFalse(project.getBuildByNumber(2).getHasArtifacts()); } @Issue("JENKINS-29922") @Test public void configRoundTrip() throws Exception { ArtifactArchiver aa = new ArtifactArchiver("*.txt"); assertNull(Util.fixEmpty(aa.getExcludes())); // null and "" behave the same, we do not care which it is assertEquals("{artifacts=*.txt}", DescribableModel.uninstantiate_(aa).toString()); // but we do care that excludes is considered to be at the default aa = j.configRoundtrip(aa); assertEquals("*.txt", aa.getArtifacts()); assertNull(Util.fixEmpty(aa.getExcludes())); assertEquals("{artifacts=*.txt}", DescribableModel.uninstantiate_(aa).toString()); aa.setExcludes("README.txt"); aa = j.configRoundtrip(aa); assertEquals("*.txt", aa.getArtifacts()); assertEquals("README.txt", aa.getExcludes()); assertEquals("{artifacts=*.txt, excludes=README.txt}", DescribableModel.uninstantiate_(aa).toString()); // TreeMap, so attributes will be sorted } static class CreateDefaultExcludesArtifact extends TestBuilder { public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException { FilePath dir = build.getWorkspace().child("dir"); FilePath subSvnDir = dir.child(".svn"); subSvnDir.mkdirs(); subSvnDir.child("file").write("content", "UTF-8"); FilePath svnDir = build.getWorkspace().child(".svn"); svnDir.mkdirs(); svnDir.child("file").write("content", "UTF-8"); dir.child("file").write("content", "UTF-8"); return true; } } @Test @Issue("JENKINS-20086") public void testDefaultExcludesOn() throws Exception { FreeStyleProject project = j.createFreeStyleProject(); Publisher artifactArchiver = new ArtifactArchiver("**", "", false, false, true, true); project.getPublishersList().replaceBy(Collections.singleton(artifactArchiver)); project.getBuildersList().replaceBy(Collections.singleton(new CreateDefaultExcludesArtifact())); assertEquals(Result.SUCCESS, build(project)); // #1 VirtualFile artifacts = project.getBuildByNumber(1).getArtifactManager().root(); assertFalse(artifacts.child(".svn").child("file").exists()); assertFalse(artifacts.child("dir").child(".svn").child("file").exists()); } @Test @Issue("JENKINS-20086") public void testDefaultExcludesOff() throws Exception { FreeStyleProject project = j.createFreeStyleProject(); ArtifactArchiver artifactArchiver = new ArtifactArchiver("**"); artifactArchiver.setDefaultExcludes(false); project.getPublishersList().replaceBy(Collections.singleton(artifactArchiver)); project.getBuildersList().replaceBy(Collections.singleton(new CreateDefaultExcludesArtifact())); assertEquals(Result.SUCCESS, build(project)); // #1 VirtualFile artifacts = project.getBuildByNumber(1).getArtifactManager().root(); assertTrue(artifacts.child(".svn").child("file").exists()); assertTrue(artifacts.child("dir").child(".svn").child("file").exists()); } @LocalData @Test public void latestOnlyMigration() throws Exception { FreeStyleProject p = j.jenkins.getItemByFullName("sample", FreeStyleProject.class); assertNotNull(p); @SuppressWarnings("deprecation") LogRotator lr = p.getLogRotator(); assertNotNull(lr); assertEquals(1, lr.getArtifactNumToKeep()); String xml = p.getConfigFile().asString(); assertFalse(xml, xml.contains("<latestOnly>")); assertTrue(xml, xml.contains("<artifactNumToKeep>1</artifactNumToKeep>")); } @LocalData @Test public void fingerprintMigration() throws Exception { FreeStyleProject p = j.jenkins.getItemByFullName("sample", FreeStyleProject.class); assertNotNull(p); String xml = p.getConfigFile().asString(); assertFalse(xml, xml.contains("<recordBuildArtifacts>")); assertTrue(xml, xml.contains("<fingerprint>true</fingerprint>")); assertFalse(xml, xml.contains("<hudson.tasks.Fingerprinter>")); ArtifactArchiver aa = p.getPublishersList().get(ArtifactArchiver.class); assertTrue(aa.isFingerprint()); FreeStyleBuild b1 = j.buildAndAssertSuccess(p); assertEquals(1, b1.getArtifacts().size()); Fingerprinter.FingerprintAction a = b1.getAction(Fingerprinter.FingerprintAction.class); assertNotNull(a); assertEquals("[stuff]", a.getFingerprints().keySet().toString()); } }