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

"No such DSL method 'call'" when calling step within dynamically loaded library a second time

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Minor Minor
    • None
    • Operating System: amazon linux 64-bit (master+workers)
      Jenkins version: 2.176.2 -- master image is built from official Jenkins LTS Docker image
      workflow-cps-plugin: 2.74
      workflow-cps-global-lib-plugin: 2.15

      My team manages a large shared pipeline library, and I'm working on breaking it up. Recently we migrated to new Jenkins infrastructure and I found that my project was no longer working.

      To demonstrate the problem, I have a global parent library P which dynamically loads child library C. P is globally configured and loaded explicitly in Jenkinsfile J using `@Library()`.

      J --> P --> C

      C contains steps X and Y; X calls Y twice. (see stepFromChild.groovy)

      P contains step Z which loads C, calls Y twice, then calls X (see fail.groovy).

      J imports P and calls Z.

      When J runs, Z calls Y twice successfully.

      But when Z calls X (which calls Y twice), the second call to Y results in "java.lang.NoSuchMethodError: No such DSL method 'call' found among steps ..." (even though globals very clearly includes the called methods – see consoleText-failing.txt).

      On an older Jenkins (2.121.1, workflow-cps 2.58 workflow-global-lib 2.9), I see no failure (see attached log consoleText-sucess.txt)

      So far I have found no workaround to this problem.

      Steps to reproduce, given a Jenkins instance with a valid GitHub credential and plugins installed:

      1. Configure a global library in "Manage Jenkins" called 'timski', pointing at https://github.com/skinitimski/library.parent
      2. Create a Pipeline job with the following pipeline script:

      @Library('timski@master') _

      env.SECRET_ID = 'github-credential-id' // TODO: replace this with the id of a valid GitHub credential

      fail()

      3. Run the job and observe a failure.

          [JENKINS-59337] "No such DSL method 'call'" when calling step within dynamically loaded library a second time

          Timothy Klopotoski created issue -
          Timothy Klopotoski made changes -
          Issue Type Original: New Feature [ 2 ] New: Bug [ 1 ]
          Timothy Klopotoski made changes -
          Description Original: My team manages a large shared pipeline library, and I'm working on breaking it up. Recently we migrated to new Jenkins infrastructure and I found that my project was no longer working.

          To demonstrate the problem, I have a global parent library *P* which [dynamically loads|https://jenkins.io/doc/book/pipeline/shared-libraries/#loading-libraries-dynamically] child library *C*. P is [globally configured|https://jenkins.io/doc/book/pipeline/shared-libraries/#global-shared-libraries] and loaded explicitly in Jenkinsfile *J* using `@Library()`.

          *J --> P --> C*

          C contains steps X and Y; X calls Y twice. (see [stepFromChild.groovy](https://github.com/skinitimski/library.child/blob/master/vars/stepFromChild.groovy))

          P contains step Z which calls X. (see [fail.groovy](https://github.com/skinitimski/library.parent/blob/master/vars/fail.groovy#L14))

          When J calls X() X(), no problem.

          But when J calls Z (which in turn calls X() X()), the second call to X results in "java.lang.NoSuchMethodError: No such DSL method 'call' found among steps ..." (even though globals very clearly includes X, and `call` isn't the right method name -- see _consoleText-failing.txt_).

          On an older Jenkins, I see no failure (see attached log _consoleText-sucess.txt_)

          So far I have found no workaround to this problem.

          *Steps to reproduce, given a Jenkins instance with a valid GitHub credential and plugins installed:*

          1. Configure a global library in "Manage Jenkins" called 'timski', pointing at https://github.com/skinitimski/library.parent
          2. Create a Pipeline job with the following pipeline script:

          bq. @Library('timski@master') _
          bq.
          bq. env.SECRET_ID = 'github-credential-id' // TODO: replace this with the id of a valid GitHub credential
          bq.
          bq. fail()

          3. Run the job and observe a failure.
          New: My team manages a large shared pipeline library, and I'm working on breaking it up. Recently we migrated to new Jenkins infrastructure and I found that my project was no longer working.

          To demonstrate the problem, I have a global parent library *P* which [dynamically loads|https://jenkins.io/doc/book/pipeline/shared-libraries/#loading-libraries-dynamically] child library *C*. P is [globally configured|https://jenkins.io/doc/book/pipeline/shared-libraries/#global-shared-libraries] and loaded explicitly in Jenkinsfile *J* using `@Library()`.

          *J --> P --> C*

          C contains steps X and Y; X calls Y twice. (see [stepFromChild.groovy|https://github.com/skinitimski/library.child/blob/master/vars/stepFromChild.groovy])

          P contains step Z which calls X. (see [fail.groovy|https://github.com/skinitimski/library.parent/blob/master/vars/fail.groovy#L14])

          When J calls X() X(), no problem.

          But when J calls Z (which in turn calls X() X()), the second call to X results in "java.lang.NoSuchMethodError: No such DSL method 'call' found among steps ..." (even though globals very clearly includes X, and `call` isn't the right method name -- see _consoleText-failing.txt_).

          On an older Jenkins, I see no failure (see attached log _consoleText-sucess.txt_)

          So far I have found no workaround to this problem.

          *Steps to reproduce, given a Jenkins instance with a valid GitHub credential and plugins installed:*

          1. Configure a global library in "Manage Jenkins" called 'timski', pointing at https://github.com/skinitimski/library.parent
          2. Create a Pipeline job with the following pipeline script:

          bq. @Library('timski@master') _
          bq.
          bq. env.SECRET_ID = 'github-credential-id' // TODO: replace this with the id of a valid GitHub credential
          bq.
          bq. fail()

          3. Run the job and observe a failure.
          Timothy Klopotoski made changes -
          Description Original: My team manages a large shared pipeline library, and I'm working on breaking it up. Recently we migrated to new Jenkins infrastructure and I found that my project was no longer working.

          To demonstrate the problem, I have a global parent library *P* which [dynamically loads|https://jenkins.io/doc/book/pipeline/shared-libraries/#loading-libraries-dynamically] child library *C*. P is [globally configured|https://jenkins.io/doc/book/pipeline/shared-libraries/#global-shared-libraries] and loaded explicitly in Jenkinsfile *J* using `@Library()`.

          *J --> P --> C*

          C contains steps X and Y; X calls Y twice. (see [stepFromChild.groovy|https://github.com/skinitimski/library.child/blob/master/vars/stepFromChild.groovy])

          P contains step Z which calls X. (see [fail.groovy|https://github.com/skinitimski/library.parent/blob/master/vars/fail.groovy#L14])

          When J calls X() X(), no problem.

          But when J calls Z (which in turn calls X() X()), the second call to X results in "java.lang.NoSuchMethodError: No such DSL method 'call' found among steps ..." (even though globals very clearly includes X, and `call` isn't the right method name -- see _consoleText-failing.txt_).

          On an older Jenkins, I see no failure (see attached log _consoleText-sucess.txt_)

          So far I have found no workaround to this problem.

          *Steps to reproduce, given a Jenkins instance with a valid GitHub credential and plugins installed:*

          1. Configure a global library in "Manage Jenkins" called 'timski', pointing at https://github.com/skinitimski/library.parent
          2. Create a Pipeline job with the following pipeline script:

          bq. @Library('timski@master') _
          bq.
          bq. env.SECRET_ID = 'github-credential-id' // TODO: replace this with the id of a valid GitHub credential
          bq.
          bq. fail()

          3. Run the job and observe a failure.
          New: My team manages a large shared pipeline library, and I'm working on breaking it up. Recently we migrated to new Jenkins infrastructure and I found that my project was no longer working.

          To demonstrate the problem, I have a global parent library *P* which [dynamically loads|https://jenkins.io/doc/book/pipeline/shared-libraries/#loading-libraries-dynamically] child library *C*. P is [globally configured|https://jenkins.io/doc/book/pipeline/shared-libraries/#global-shared-libraries] and loaded explicitly in Jenkinsfile *J* using `@Library()`.

          *J --> P --> C*

          C contains steps X and Y; X calls Y twice. (see [stepFromChild.groovy|https://github.com/skinitimski/library.child/blob/master/vars/stepFromChild.groovy])

          P contains step Z which calls X. (see [fail.groovy|https://github.com/skinitimski/library.parent/blob/master/vars/fail.groovy#L14])

          When J calls X() X(), no problem.

          But when J calls Z (which in turn calls X() X()), the second call to X results in "java.lang.NoSuchMethodError: No such DSL method 'call' found among steps ..." (even though globals very clearly includes X, and `call` isn't the right method name -- see _consoleText-failing.txt_).

          On an older Jenkins, I see no failure (see attached log _consoleText-sucess.txt_)

          So far I have found no workaround to this problem.

          *Steps to reproduce, given a Jenkins instance with a valid GitHub credential and plugins installed:*

          1. Configure a global library in "Manage Jenkins" called 'timski', pointing at https://github.com/skinitimski/library.parent
          2. Create a Pipeline job with the following pipeline script:

          bq. @Library('timski@master') _
          bq. env.SECRET_ID = 'github-credential-id' // TODO: replace this with the id of a valid GitHub credential
          bq. fail()

          3. Run the job and observe a failure.

          It is worth noting that there are other ways to produce the issue, but above was the most trivial way I could find so far.

          Timothy Klopotoski added a comment - It is worth noting that there are other ways to produce the issue, but above was the most trivial way I could find so far.
          Timothy Klopotoski made changes -
          Description Original: My team manages a large shared pipeline library, and I'm working on breaking it up. Recently we migrated to new Jenkins infrastructure and I found that my project was no longer working.

          To demonstrate the problem, I have a global parent library *P* which [dynamically loads|https://jenkins.io/doc/book/pipeline/shared-libraries/#loading-libraries-dynamically] child library *C*. P is [globally configured|https://jenkins.io/doc/book/pipeline/shared-libraries/#global-shared-libraries] and loaded explicitly in Jenkinsfile *J* using `@Library()`.

          *J --> P --> C*

          C contains steps X and Y; X calls Y twice. (see [stepFromChild.groovy|https://github.com/skinitimski/library.child/blob/master/vars/stepFromChild.groovy])

          P contains step Z which calls X. (see [fail.groovy|https://github.com/skinitimski/library.parent/blob/master/vars/fail.groovy#L14])

          When J calls X() X(), no problem.

          But when J calls Z (which in turn calls X() X()), the second call to X results in "java.lang.NoSuchMethodError: No such DSL method 'call' found among steps ..." (even though globals very clearly includes X, and `call` isn't the right method name -- see _consoleText-failing.txt_).

          On an older Jenkins, I see no failure (see attached log _consoleText-sucess.txt_)

          So far I have found no workaround to this problem.

          *Steps to reproduce, given a Jenkins instance with a valid GitHub credential and plugins installed:*

          1. Configure a global library in "Manage Jenkins" called 'timski', pointing at https://github.com/skinitimski/library.parent
          2. Create a Pipeline job with the following pipeline script:

          bq. @Library('timski@master') _
          bq. env.SECRET_ID = 'github-credential-id' // TODO: replace this with the id of a valid GitHub credential
          bq. fail()

          3. Run the job and observe a failure.
          New: My team manages a large shared pipeline library, and I'm working on breaking it up. Recently we migrated to new Jenkins infrastructure and I found that my project was no longer working.

          To demonstrate the problem, I have a global parent library *P* which [dynamically loads|https://jenkins.io/doc/book/pipeline/shared-libraries/#loading-libraries-dynamically] child library *C*. P is [globally configured|https://jenkins.io/doc/book/pipeline/shared-libraries/#global-shared-libraries] and loaded explicitly in Jenkinsfile *J* using `@Library()`.

          *J --> P --> C*

          C contains steps X and Y; X calls Y twice. (see [stepFromChild.groovy|https://github.com/skinitimski/library.child/blob/master/vars/stepFromChild.groovy])

          P contains step Z which loads C, calls Y twice, then calls X (see [fail.groovy|https://github.com/skinitimski/library.parent/blob/master/vars/fail.groovy#L14]).

          J imports P and calls Z.

          When J runs, Z calls Y twice successfully.

          But when Z calls X (which calls Y twice), the second call to Y results in "java.lang.NoSuchMethodError: No such DSL method 'call' found among steps ..." (even though globals very clearly includes the called methods -- see _consoleText-failing.txt_).

          On an older Jenkins (2.121.1, workflow-cps 2.58 workflow-global-lib 2.9), I see no failure (see attached log _consoleText-sucess.txt_)

          So far I have found no workaround to this problem.

          *Steps to reproduce, given a Jenkins instance with a valid GitHub credential and plugins installed:*

          1. Configure a global library in "Manage Jenkins" called 'timski', pointing at https://github.com/skinitimski/library.parent
          2. Create a Pipeline job with the following pipeline script:

          bq. @Library('timski@master') _
          bq. env.SECRET_ID = 'github-credential-id' // TODO: replace this with the id of a valid GitHub credential
          bq. fail()

          3. Run the job and observe a failure.

          If I modify the Jenkinsfile to import the parent and the child library, so that the dynamic import is redundant (i.e, it prints "Only using first definition of library child"), everything works.

          So, this problem seems scoped to libraries dynamically loaded using the `library` step.

          Timothy Klopotoski added a comment - If I modify the Jenkinsfile to import the parent and the child library, so that the dynamic import is redundant (i.e, it prints "Only using first definition of library child"), everything works. So, this problem seems scoped to libraries dynamically loaded using the `library` step .

          Timothy Klopotoski added a comment - - edited

          We've found a workaround to this problem.

          As a convention, with extensibility in mind, my team always uses the following signature when writing custom steps:

          def call(Map args) {

          This allows us to add optional step parameters later. We use this even for parameterless steps, in case we want to accept new parameters later on.

          The workaround is to change the signature to explicitly accept no parameters (`def call() {`). This results in no errors.

          It still seems to me that using the named args signature should work.

          Timothy Klopotoski added a comment - - edited We've found a workaround to this problem. As a convention, with extensibility in mind, my team always uses the following signature when writing custom steps : def call(Map args) { This allows us to add optional step parameters later. We use this even for parameterless steps, in case we want to accept new parameters later on. The workaround is to change the signature to explicitly accept no parameters (`def call() {`). This results in no errors. It still seems to me that using the named args signature should work.
          Tom Turner made changes -
          Attachment New: jenkins-dsl-error.txt [ 50125 ]

          Tom Turner added a comment -

          My work recently updated to 2.190.3.2 and I can confirm duplicate calls to the same declarative function can break shared libraries.  I ended up with the same workaround of removing the argument in the call() method but also needed to do another change.

          Changing

          def call(body) {

          to

          def call() {

          The other change involved removing an apostrophe in an echo. 

          echo "an apostrophe's apostrophe causes DSL failure"

          Attached is the stack trace.jenkins-dsl-error.txt

          Tom Turner added a comment - My work recently updated to 2.190.3.2 and I can confirm duplicate calls to the same declarative function can break shared libraries.  I ended up with the same workaround of removing the argument in the call() method but also needed to do another change. Changing def call(body) { to def call() { The other change involved removing an apostrophe in an echo.  echo "an apostrophe's apostrophe causes DSL failure" Attached is the stack trace. jenkins-dsl-error.txt

            Unassigned Unassigned
            skinitimski Timothy Klopotoski
            Votes:
            3 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: