Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-64770

Replay with conditional load step

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Minor Minor
    • workflow-cps-plugin
    • None

      Hi!

      In the current implementation, load step generate ordered class names for loaded scripts ignoring file pathname. That leads to the problem with a replay if load steps get executed based on condition and can be skipped:

      Let's say we have a pipeline with three stages that contains two load step calls

       

      node {
          stage('Build') {
              if (!isAlreadyBuilt) {
                  load('scripts/build.groovy')() // (1) load call
              }
          }
      
      
          stage('Deploy') {
              load('scripts/deploy.groovy') // (2) load call
          }
      }
      
      

       

      Following the logic from here https://github.com/jenkinsci/workflow-cps-plugin/blob/261df120d1c2f228b2137c3f880a7741293ea5f2/src/main/java/org/jenkinsci/plugins/workflow/cps/steps/LoadStepExecution.java#L39-L41

      String text = cwd.child(step.getPath()).readToString();
      String clazz = execution.getNextScriptName(step.getPath()); // ignores parameter and return ordered generated name Script1, Script2, ... 
      String newText = ReplayAction.replace(execution, clazz);
      if (newText != null) {
          listener.getLogger().println("Replacing Groovy text with edited version");
          text = newText;
      }
      

       

       

      First run:

      (1) 

      text = the content of `scripts/build.groovy`
      clazz = 'Script1'
      newText = null (since it's not a replay)
      if (newText != null) {...} // skipping...
      
      
      Result: text = scripts/build.groovy
      

      (2)

      text = the content of `scripts/deploy.groovy`
      clazz = 'Script2'
      newText = null (since it's not a replay)
      if (newText != null) {...} // skipping...
       
      Result: text = the content of `scripts/deploy.groovy`
      

       

       

      Replay:

      (1) Will be skipped due to condition in the pipeline

      (2)

      text = the content of `scripts/deploy.groovy`
      clazz = 'Script1' // because it's a first call of load step
      newText = the content of Script1(scripts/build.groovy)
      if (newText != null) {
        listener.getLogger().println("Replacing Groovy text with edited version");      
        text = newText; // text get replaced from the content of deploy.groovy to build.groovy
      }
       
      Result: text = the content of `scripts/build.groovy`
      

       

      As a result script `scripts/build.groovy` will be executed during the replay build instead of expected `scripts/deploy.groovy`.

      A workaround here is to load all external scripts before the conditional logic that helps to keep consistent order for external scripts class names no matter whether we use them or not during a build.

       

      node {
          def externalScripts = [:]
          stage('Load external scripts') {
              externalScripts = ['build': load 'scripts/build.groovy',
                                 'deploy': load 'scripts/deploy.groovy']
              // 'build' is always Script1, 'deploy' is always Script2]
          }
          stage('Build') {
              if (!isAlreadyBuilt) {
                 externalScripts['build']() // Script1
              }
          }
      
      
          stage('Deploy') {
              externalScripts['deploy']() // Script2
          }
      }
      
      

       

      Would be nice to have a fix for load step unexpected behaviour or at least update documentation with a mention of this.

            Unassigned Unassigned
            prizov Alex
            Votes:
            1 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: