0
votes

I try to create a zip file within a shared library for jenkins pipeline with the help of AntBuilder. A simple zip file can be created, but as soon as I try to use a block it does not work. I don't get any error message or Exceptions. How can I debug a Pipepeline-Script? Or how can I solve the issue?

My Code looks as follow (step zipFolder doesn't work, step zipFolder2 does work)

Jenkinsfile @Library('utils') _

pipeline {
    agent any 
    stages {
        stage('Build') { 
            steps {
                zipFolder( )
                zipFolder2( )
            }
        }

    }
}

shared library:

vars/zipFolder.groovy:

import com.example.Utilities

def call(){
  new Utilities(this).zip(pwd())
}

vars/zipFolder2.groovy

import com.example.Utilities

def call(){
  new Utilities(this).zip2(pwd())
}

src/com/example/Utilities.groovy

package com.example
import groovy.util.AntBuilder
import org.apache.tools.ant.DefaultLogger

class Utilities implements Serializable {
  def steps
  def byteStream

  Utilities(steps) {this.steps = steps}

  def zip(baseDir){
    def ant = setupAnt(baseDir)
    def destFolder = "${baseDir}/Dist"
    def destfile = "${destFolder}/test.zip"

    ant.delete dir: destFolder
    ant.mkdir(dir: destFolder)
    ant.zip(destfile: destfile, whenempty: 'create', excludes: destfile) {
      zipfileset (dir: "${baseDir}/install", includes: "test.txt",  erroronmissingdir: false)
   }

    steps.echo "Zip1"
    steps.echo "Ant-Result: " + byteStream.toString()
  }

  def zip2(baseDir){
    def ant = setupAnt(baseDir)
    def destFolder = "${baseDir}/Dist2"
    def destfile = "${destFolder}/test.zip"

    ant.delete dir: destFolder
    ant.mkdir(dir: destFolder)
    ant.zip(destfile: destfile, whenempty: 'create', excludes: destfile, basedir: baseDir)

    steps.echo "Zip2"
    steps.echo "Ant-Result: " + byteStream.toString()
  }

  def setupAnt(baseDir){
    def ant = new AntBuilder()
    byteStream = new ByteArrayOutputStream()
    def printStream = new PrintStream( byteStream )
    def project = ant.project

    project.buildListeners.each {
        if ( it instanceof DefaultLogger ) {
                it.setMessageOutputLevel(org.apache.tools.ant.Project.MSG_DEBUG)
                it.setOutputPrintStream printStream
                it.setErrorPrintStream printStream
        }
    }

    ant
  }


}
2

2 Answers

1
votes

AntBuilder is not Serializable. What we did is encapsulate the call to AntBuilder inside an extra groovy script which is created on the fly and called by calling the groovy binary like:

writeFile file:createZip.groovy text:”””
def antBuilder = new AntBuilder()
...
“””
sh ‘groovy createZip.groovy’

You could also consider using the Jenkins Pipeline zip step. See https://jenkins.io/doc/pipeline/steps/pipeline-utility-steps/#zip-create-zip-file

0
votes

With the hint of Joerg S I solved the issue with the following Code:

src/com/example/Utilities.groovy

package com.example

import org.apache.tools.ant.DefaultLogger


package com.example

import org.apache.tools.ant.DefaultLogger


class Utilities implements Serializable {
  def steps
  def byteStream

  Utilities(steps) {this.steps = steps}


  def zip(baseDir, Closure callback ){
    def ant = setupAnt()
    def destFolder = "${baseDir}/Dist"
    def destfile = "${destFolder}/test.zip"

    ant.delete dir: destFolder
    ant.mkdir(dir: destFolder)

    ant.zip(destfile: destfile, whenempty: 'create', excludes: destfile, callback)

    steps.echo "Ant-Result: " + byteStream.toString()
  }


  def setupAnt(){
    def ant = new AntBuilder()
    byteStream = new ByteArrayOutputStream()
    def printStream = new PrintStream( byteStream )
    def project = ant.project

    project.buildListeners.each {
        if ( it instanceof DefaultLogger ) {
                it.setMessageOutputLevel(org.apache.tools.ant.Project.MSG_DEBUG)
                it.setOutputPrintStream printStream
                it.setErrorPrintStream printStream
        }
    }

    ant
  }


}

vars/zipFolder.groovy

import com.example.Utilities

def call(Closure callback){
  new Utilities(this).zip(pwd(), callback)
}

Jenkinsfile

@Library('utils') _

pipeline {
    agent any 
    stages {
        stage('Build') { 
            steps {
                cleanWs()
                writeFile file: 'test.txt', text: 'Sample Text'
                writeFile file: 'test2.txt', text: 'Sample Text 2'
                writeFile file: 'src/test3.txt', text: 'Sample Text 3'

                zipIt()

            }
        }

    }
}

@NonCPS
def zipIt(){
    zipFolder( ) {
        delegate.zipfileset (dir: "${pwd()}", includes: "test.txt", excludes: "src/**" , filemode: "0755", prefix: "bin/examples", erroronmissingdir: false)
        delegate.zipfileset (dir: "${pwd()}/src", includes: "*.txt",  erroronmissingdir: false)
    }
}

Important is the use of the annotation @NonCPS and delegate.zipfileset instead of simply using zipfileset.

Edit: This solution does not work on a slave. This code is executed at the master server, even if the build is run on a slave.