7

We have some complex bash script which is now in our managed files section in Jenkins. We try to migrate the job to a pipeline but we don't know enough to translate the bash script to groovy so we want to keep this in bash. We have a jenkins-shared-library in Git in which we store our pipeline templates. Inside the job we add the right environment variables.

We want to keep our bash script in git instead of in the managed files. What is the right way to load this script in the pipeline and execute it? We tried some stuff with libraryResource, but we didn't manage to make it work. Where do we have to put the test.sh script in git and how can we call it? (or is it completely wrong to run a shell script here)

def call(body) {
    // evaluate the body block, and collect configuration into the object
    def pipelineParams= [:]
    body.resolveStrategy = Closure.DELEGATE_FIRST
    body.delegate = pipelineParams
    body()

    pipeline {
        agent any

        options {
            buildDiscarder(logRotator(numToKeepStr: '3'))
        }

        stages {

            stage ('ExecuteTestScript') {
                steps {
                    def script = libraryResource 'loadtestscript?'

                    script {
                        sh './test.sh'
                    }
                }
            }

        }

        post {
            always {
                cleanWs()
            }

        }

    }
}
mkobit
  • 43,979
  • 12
  • 156
  • 150
DenCowboy
  • 13,884
  • 38
  • 114
  • 210
  • Where did you put the script in your shared library? What didn't work with `libraryResource`? – mkobit Nov 23 '17 at 16:13
  • @mkobit the path was wrong. It's working now. Now I can execute the script with `sh "bash -c '$script'"` but the environment variables which are know inside the jenkinsfile aren't known inside the script this way – DenCowboy Nov 23 '17 at 19:28
  • You could possibly pipe the script to bash - `sh "echo '$script' | bash"` – mkobit Nov 24 '17 at 17:48
  • Does this answer your question? [How to invoke bash functions defined in a resource file from a Jenkins pipeline Groovy script?](https://stackoverflow.com/questions/40213654/how-to-invoke-bash-functions-defined-in-a-resource-file-from-a-jenkins-pipeline) – Big McLargeHuge Jul 04 '20 at 15:07
  • I don't understand why there is a `pipeline` inside a `def`, not familiar with that syntax, I'd expect it to be the other way around. – Nagev Nov 23 '20 at 18:27

5 Answers5

6

In my company we also have complex bash scripts in our CI and libraryResource was a better solution. Following your script, you can perform some changes in order to use a bash script stored into libraryResource:

stages {
    stage ('ExecuteTestScript') {
        steps {
            // Load script from library with package path
            def script_bash = libraryResource 'com/example/loadtestscript'

            // create a file with script_bash content
            writeFile file: './test.sh', text: script_bash

            // Run it!
            sh 'bash ./test.sh'
        }
    }
}
Barizon
  • 146
  • 2
  • 7
0

I want to elaborate on @Barizon answer, that pointed me in the right direction.

My need was to execute the script on a remote service with ssh.

I created a groovy script inside the /var folder of the shared library project, let's call it my_script.groovy.

Inside the script i defined the funciton:

def my_function(String serverIp, String scriptArgument) {
    def script_content = libraryResource 'my_scripts/test.sh'
    // create a file with script_bash content
    writeFile file: './test.sh', text: script_content
    echo "Execute remote script test.sh..."
    def sshCommand = "ssh username@${serverIp} \'bash -xs\' < ./test.sh ${scriptArgument}"
    echo "Ssh command is: ${sshCommand}"
    sh(sshCommand)
}

From the pipeline I can invoke it like this:

@Library('MySharedLibrary')_
pipeline {
  agent any
  stages {
    stage('MyStage') {
        steps {
            script {
                my_script.my_function("192.168.1.1", "scriptArgumentValue")
            }
        }
    }
  }
}
Ena
  • 3,481
  • 36
  • 34
0

Instead of copying the script to your workspace, you can call the script directly from a custom step. For example, create a file in the library's vars directory named doSomething.groovy:

#!/usr/bin/env groovy

def call(args) {
    def scriptDir = WORKSPACE + '@libs/my-shared-library'
    sh "$scriptDir/do-something.sh $args"
}

This works because the shared library is checked out to a directory named after the job's workspace suffixed with @libs. If you'd prefer, you can move do-something.sh to the library's resources directory or whatever.

Big McLargeHuge
  • 14,841
  • 10
  • 80
  • 108
0

I don't know if this is not possible for the OP but I find it much simpler to just keep my utility scripts in the same git repository as the Jenkinsfile, I never had to use libraryResource. Then you can just call them directly with the sh directive, and you can even pass variables defined in the environment block. For example, inside a script or steps block, within a pipeline stage:

sh "./build.sh $param1"

You could also put a bunch of bash functions in its own file, say "scripts/myfuncs.sh". You can even have a groovy function that calls such scripts, essentially being a wrapper, see below:

def call_func() {
  sh"""
    #!/bin/bash

    . ./scripts/myfuncs.sh
    my_util_func "$param1"
  """
}

A few things to note:

Although on my terminal I can use source scripts/myfuncs.sh, on Jenkins I need to use the . shorthand for source (as shown above) otherwise it complains that it cannot find source!

You must make ./scripts/myfuncs.sh executable, e.g. chmod 755 ./scripts/myfuncs.sh before pushing it to the repository.

Example with full pipeline:

pipeline {
  agent any
  stages {
    stage('Build') {
      steps {
        sh '''
          ./build.sh
        '''
      }
    }
  }
  post {
    always {
      sh '''
        ./scripts/clean.sh
      '''
    }
    success {
      script {
        if (env.BRANCH_NAME == 'master') {
          sh "./scripts/master.sh"
        } else if (env.BRANCH_NAME.startsWith('PR')) {
          sh "./scripts/mypr.sh"
        } else {
          // this is some other branch
        }
      }
    }
  }
}

In the above example, both "Jenkinsfile" and "build.sh" are in the root of the repository.

Nagev
  • 10,835
  • 4
  • 58
  • 69
  • The reason you need to use `.` instead of `source` is that it's running it with the system default `sh` instead of with `bash`. It does support using `#!` to pick the interpreter, but it must be the first two characters in the file, and because of the way you quoted the script it isn't. If you started with `sh"""#!/bin/bash` instead then you could use `source`. – Jason Kohles Sep 22 '21 at 13:55
  • That makes sense, thank you! – Nagev Sep 22 '21 at 15:13
0

you can use from bitbucket or git . create a repository in bitbucket or git and config in jenkins job .and write body of sh in script block in pipeline

enter image description here

then you can config jenkins file and used test.sh

enter image description here