diff --git a/job-dsl-plugin/build.gradle b/job-dsl-plugin/build.gradle index 4610f8ddb..946b4b867 100644 --- a/job-dsl-plugin/build.gradle +++ b/job-dsl-plugin/build.gradle @@ -44,6 +44,7 @@ jenkinsPlugin { dependencies { compile project(':job-dsl-core') + optionalJenkinsPlugins([group: 'org.jenkins-ci.plugins', name: 'ant', version: '1.2', ext: 'jar']) // see JENKINS-17129 optionalJenkinsPlugins([group: 'org.jenkins-ci.plugins', name: 'cloudbees-folder', version: '4.0', ext: 'jar']) optionalJenkinsPlugins([group: 'org.jenkins-ci.plugins', name: 'credentials', version: '1.6', ext: 'jar']) } diff --git a/job-dsl-plugin/src/main/groovy/javaposse/jobdsl/plugin/ScriptRequestGenerator.groovy b/job-dsl-plugin/src/main/groovy/javaposse/jobdsl/plugin/ScriptRequestGenerator.groovy index 58bee6149..d48eb4e3c 100644 --- a/job-dsl-plugin/src/main/groovy/javaposse/jobdsl/plugin/ScriptRequestGenerator.groovy +++ b/job-dsl-plugin/src/main/groovy/javaposse/jobdsl/plugin/ScriptRequestGenerator.groovy @@ -19,10 +19,8 @@ class ScriptRequestGenerator { public Set getScriptRequests(String targets, boolean usingScriptText, String scriptText, boolean ignoreExisting) throws IOException, InterruptedException { Set scriptRequests = Sets.newHashSet(); - String jobName = build.getProject().getName(); - URL workspaceUrl = new URL(null, "workspace://" + jobName + "/", new WorkspaceUrlHandler()); - if(usingScriptText) { + URL workspaceUrl = WorkspaceProtocol.createWorkspaceUrl(build.project) ScriptRequest request = new ScriptRequest(null, scriptText, workspaceUrl, ignoreExisting); scriptRequests.add(request); } else { @@ -30,8 +28,7 @@ class ScriptRequestGenerator { FilePath[] filePaths = build.getWorkspace().list(targetsStr.replace("\n", ",")); for (FilePath filePath : filePaths) { - String relativePath = filePath.parent.getRemote() - build.getWorkspace().getRemote() - URL relativeWorkspaceUrl = new URL(workspaceUrl, relativePath + "/") + URL relativeWorkspaceUrl = WorkspaceProtocol.createWorkspaceUrl(build, filePath.parent) ScriptRequest request = new ScriptRequest(filePath.name, null, relativeWorkspaceUrl, ignoreExisting); scriptRequests.add(request); } diff --git a/job-dsl-plugin/src/main/groovy/javaposse/jobdsl/plugin/WorkspaceProtocol.groovy b/job-dsl-plugin/src/main/groovy/javaposse/jobdsl/plugin/WorkspaceProtocol.groovy new file mode 100644 index 000000000..a261db579 --- /dev/null +++ b/job-dsl-plugin/src/main/groovy/javaposse/jobdsl/plugin/WorkspaceProtocol.groovy @@ -0,0 +1,51 @@ +package javaposse.jobdsl.plugin + +import hudson.FilePath +import hudson.model.AbstractBuild +import hudson.model.AbstractProject +import jenkins.model.Jenkins + +class WorkspaceProtocol { + + /** + * Create a workspace URL that represents the base dir of the given AbstractProject. + */ + static URL createWorkspaceUrl(AbstractProject project) { + String jobName = project.fullName + String encodedJobName = URLEncoder.encode(jobName, "UTF-8") + new URL(null, "workspace://$encodedJobName/", new WorkspaceUrlHandler()) + } + + /** + * Create a workspace URL that represents the given FilePath. + */ + static URL createWorkspaceUrl(AbstractBuild build, FilePath filePath) { + String relativePath = filePath.getRemote() - build.workspace.getRemote() + relativePath = relativePath.replaceAll('\\\\', '/') // normalize for Windows + new URL(createWorkspaceUrl(build.project), "$relativePath/", new WorkspaceUrlHandler()) + } + + /** + * Parse the AbstractProject from the given workspace URL representation. + */ + static AbstractProject getProjectFromWorkspaceUrl(URL url) { + Jenkins jenkins = Jenkins.instance + if (!jenkins) { + throw new IllegalStateException("Not in a running Jenkins") + } + + String jobName = url.host + String decodedJobName = URLDecoder.decode(jobName, "UTF-8") + (AbstractProject) Jenkins.instance.getItemByFullName(decodedJobName) + } + + /** + * Parse the FilePath from the given workspace URL representation. + */ + static FilePath getFilePathFromUrl(URL url) { + AbstractProject project = getProjectFromWorkspaceUrl(url) + FilePath workspace = project.someWorkspace + String relativePath = url.file[1..-1] // remove leading slash + workspace.child relativePath + } +} diff --git a/job-dsl-plugin/src/main/groovy/javaposse/jobdsl/plugin/WorkspaceUrlConnection.java b/job-dsl-plugin/src/main/groovy/javaposse/jobdsl/plugin/WorkspaceUrlConnection.java index c91e05e0b..99340c08e 100644 --- a/job-dsl-plugin/src/main/groovy/javaposse/jobdsl/plugin/WorkspaceUrlConnection.java +++ b/job-dsl-plugin/src/main/groovy/javaposse/jobdsl/plugin/WorkspaceUrlConnection.java @@ -1,8 +1,6 @@ package javaposse.jobdsl.plugin; import hudson.FilePath; -import hudson.model.AbstractProject; -import jenkins.model.Jenkins; import java.io.FileNotFoundException; import java.io.IOException; @@ -19,22 +17,12 @@ public WorkspaceUrlConnection(URL url) { @Override public void connect() throws IOException { - String jobName = url.getHost(); - Jenkins jenkins = Jenkins.getInstance(); - if(jenkins == null) { - throw new IllegalStateException("Not in a running Jenkins"); - } - AbstractProject project = (AbstractProject) jenkins.getItem(jobName); - FilePath workspace = project.getSomeWorkspace(); - - String path = url.getFile(); - String relativePath = path.substring(1, path.length()); - FilePath targetPath = workspace.child(relativePath); + FilePath targetPath = WorkspaceProtocol.getFilePathFromUrl(url); // Make sure we can find the file try { if (!targetPath.exists()) { - throw new FileNotFoundException("Unable to find file at " + path); + throw new FileNotFoundException("Unable to find file at " + targetPath); } } catch (InterruptedException e) { throw new IOException(e); diff --git a/job-dsl-plugin/src/test/groovy/javaposse/jobdsl/plugin/WorkspaceProtocolSpec.groovy b/job-dsl-plugin/src/test/groovy/javaposse/jobdsl/plugin/WorkspaceProtocolSpec.groovy index 429d349c3..b71d1864e 100644 --- a/job-dsl-plugin/src/test/groovy/javaposse/jobdsl/plugin/WorkspaceProtocolSpec.groovy +++ b/job-dsl-plugin/src/test/groovy/javaposse/jobdsl/plugin/WorkspaceProtocolSpec.groovy @@ -1,35 +1,102 @@ package javaposse.jobdsl.plugin -import javaposse.jobdsl.dsl.DslScriptLoader -import javaposse.jobdsl.dsl.FileJobManagement -import javaposse.jobdsl.dsl.JobManagement -import javaposse.jobdsl.dsl.ScriptRequest +import com.cloudbees.hudson.plugins.folder.Folder +import hudson.FilePath +import hudson.model.AbstractBuild +import hudson.model.AbstractProject +import hudson.model.FreeStyleProject +import org.junit.Rule +import org.jvnet.hudson.test.JenkinsRule import spock.lang.Specification -class WorkspaceProtocolSpec extends Specification { +class WorkspaceProtocolSpec extends Specification { + @Rule + public JenkinsRule jenkinsRule = new JenkinsRule() + + def 'url for project'() { + given: + AbstractProject project = jenkinsRule.createFreeStyleProject('test-project') + + when: + URL url = WorkspaceProtocol.createWorkspaceUrl(project) + + then: + url.host == 'test-project' + url.file == '/' + + when: + AbstractProject returnedProject = WorkspaceProtocol.getProjectFromWorkspaceUrl(url) + + then: + project == returnedProject + } + + def 'url for project in folder'() { + given: + Folder folder = jenkinsRule.jenkins.createProject Folder, 'folder' + AbstractProject project = folder.createProject FreeStyleProject, 'test-project' + + when: + URL url = WorkspaceProtocol.createWorkspaceUrl(project) + + then: + url.host == 'folder%2Ftest-project' + url.file == '/' + + when: + AbstractProject returnedProject = WorkspaceProtocol.getProjectFromWorkspaceUrl(url) + + then: + project == returnedProject + } + + def 'url for project with file'() { + given: + AbstractProject project = jenkinsRule.createFreeStyleProject('test-project') + AbstractBuild build = project.createExecutable() + FilePath workspace = jenkinsRule.jenkins.getWorkspaceFor(project) + build.setWorkspace workspace + FilePath filePath = workspace.child('files') - def 'load workspace url'() { when: - URL url = new URL(null, "workspace://JOB/dir/file.dsl", new WorkspaceUrlHandler()) + URL url = WorkspaceProtocol.createWorkspaceUrl(build, filePath) then: - url.host == 'JOB' - url.file == '/dir/file.dsl' + url.host == 'test-project' + url.file == '/files/' + + when: + AbstractProject returnedProject = WorkspaceProtocol.getProjectFromWorkspaceUrl(url) + FilePath returnedFilePath = WorkspaceProtocol.getFilePathFromUrl(url) + + then: + project == returnedProject + filePath == returnedFilePath } - def 'reference workspace form dsl'() { - def resourcesDir = new File("src/test/resources") - JobManagement jm = new FileJobManagement(resourcesDir) - URL url = new URL(null, "workspace://JOB/dir/file.dsl", new WorkspaceUrlHandler()) + def 'url for project in folder with file'() { + given: + Folder folder = jenkinsRule.jenkins.createProject Folder, 'folder' + AbstractProject project = folder.createProject FreeStyleProject, 'test-project' + AbstractBuild build = project.createExecutable() + FilePath workspace = jenkinsRule.jenkins.getWorkspaceFor(project) + build.setWorkspace workspace + FilePath filePath = workspace.child('files') - setup: - ScriptRequest request = new ScriptRequest('caller.dsl', null, url); + when: + URL url = WorkspaceProtocol.createWorkspaceUrl(build, filePath) + + then: + url.host == 'folder%2Ftest-project' + url.file == '/files/' when: - DslScriptLoader.runDslEngine(request, jm) + AbstractProject returnedProject = WorkspaceProtocol.getProjectFromWorkspaceUrl(url) + FilePath returnedFilePath = WorkspaceProtocol.getFilePathFromUrl(url) then: - thrown(IllegalStateException) + project == returnedProject + filePath == returnedFilePath } }