Quickly trying out your suggestion of pipeline directly inside the if. It 
sees the stages, but something is not right.
It doesn't see/execute the environment section (which means the use of 
credentials('cred') isn't being loaded.

Here is what I see when the pipeline {} is on it's own:

[Pipeline] withEnv
[Pipeline] {
[Pipeline] withCredentials
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Feature Build)


Here is what I see when I wrap a simple if around the pipeline:

[Pipeline] stage
[Pipeline] { (Feature Build)
[Pipeline] node


Am I missing something or should I file a bug?




On Wednesday, April 12, 2017 at 4:53:23 PM UTC-4, Patrick Wolf wrote:
>
> Feel free to open a JIRA ticket but I'm not a huge fan of this because it 
> is counter to the KISS principle we wanted with Declarative and breaks the 
> Blue Ocean editor.  We have discussed having multiple "stages" blocks but 
> rejected that because it quickly becomes needlessly complex without adding 
> any use case coverage. IMO, having multiple "stages" makes much more sense 
> than having multiple "pipelines" or else you will have to recreate all 
> agent, environment, libraries, options, parameters etc for each pipeline 
> and that leads to wanting those sections being DRY as well and Declarative 
> pretty much falls apart completely.
>
> BTW, It is already possible to have multiple 'pipeline' closures in a 
> single Jenkinsfile but they will be treated as parts of a whole Pipeline 
> and this cannot be used in the editor.  Because the Jenkinsfile is treated 
> as one continuous Pipeline anything outside of the pipeline closures is 
> interpreted as Scripted Pipeline. This means you can use 'if' blocks around 
> the separate 'pipeline' blocks instead of using 'load' if you choose but 
> keeping them in separate files makes maintenance easier, I think.
>
> if (BRANCH_NAME.startsWith("develop")) {
>     pipeline { .... }
> } 
>
>
> Also, it's worth noting that 'readTrusted' probably works better than 
> 'load' because this takes the committer into account and it doesn't require 
> a workspace.
>
>
> https://jenkins.io/doc/pipeline/steps/workflow-multibranch/#code-readtrusted-code-read-trusted-file-from-scm
>
> As for DRY stages there are several ways to accomplish this with Pipeline.
>
> 1. Shared Library and Resources - This is the preferred method of creating 
> DRY routines
>
> You create a global variable that has all of the steps you want (with 
> appropriate variable replacement for environment variables). You could have 
> a build.groovy global variable in the /vars directory that does all of your 
> build steps. Then the steps in your stage can be single line.
>
> Alternatively, you can store shell scripts in the /resources of your 
> shared library and run those in your steps without having to duplicate 
> anything:
>
> https://gist.github.com/HRMPW/92231e7b2344f20d9cc9d5f2eb778a54
>
> 2. You can define your steps directly in the Jenkinsfile at the top level 
> either as strings or methods and simply call that method from with each 
> pipeline.
>
> 3. You can define your steps in a configuration file as a property or yaml 
> and load those files using the Pipeline utility steps plugin. 
> https://wiki.jenkins-ci.org/display/JENKINS/Pipeline+Utility+Steps+Plugin
>
> To sum up, I think having different stages is worth discussing (it is not 
> going to be implemented in the short term) but there are already many 
> existing ways to make Pipelines DRY.
>
> On Tuesday, April 11, 2017 at 8:43:49 AM UTC-7, Kenneth Brooks wrote:
>>
>> TL;DR up front:
>> *As a user, I want to have a pipeline that performs specific pipeline 
>> stages based on the branch. Recommendation: Put the when{} condition 
>> outside the pipeline{} tag.*
>> *As a user, I want to declare my stages but have the implementation be 
>> separate so that I can reuse them in multiple pipelines*. 
>>
>> Currently the Declarative syntax has the ability to perform a stage 
>> conditionally using 'when' but not a whole pipeline.
>> This leads to making the pipeline fairly inflexible and much harder to 
>> read thru.
>>
>> Take for example:
>>
>> pipeline {
>>
>>    stages {
>>      stage('Build') {
>>        when { branch "develop || master || feature"} // no the real syntax, 
>> i know
>>        steps { /* do some build stuff */ }
>>      }
>>
>>      stage('Scan') {
>>        when { branch "master"}
>>        steps { /* run static code analysis or other code scanning */}
>>      }
>>
>>      stage('Pull Request Build') {
>>        when { branch "PR-*"}
>>        steps { /* do a merge build stuff */ }
>>      }
>>
>>      stage('Dev Deploy') {
>>        when { branch "develop || master"}
>>        steps { /* deploy to dev */ }
>>      }
>>
>>      stage('Pull Request Deploy') {
>>        when { branch "PR-*"}
>>        steps { /* deploy to special PR sandbox */}
>>      }
>>   }
>> }
>>
>>
>> In this simple example, the following will happen, but it is extremely hard 
>> to follow.
>>
>> Feature -> Build
>> Master -> Build, Scan, Dev Deploy
>> Develop -> Build, Dev Deploy
>> Pull Request -> Pull Request Build, Pull Request Deploy
>>
>> I would suggest we allow the when to be placed at the pipeline level 
>> somehow.args
>>
>> pipeline('master') { // Just for naming
>>   when { branch "master" }
>>   stages {
>>     stage('Build'){
>>       steps { /* do some build stuff */ }
>>     }
>>     stage('Scan'){
>>       steps { /* run static code analysis or other code scanning */}
>>     }
>>     stage('Dev Deploy'){
>>       steps { /* deploy to dev */ }
>>     }
>>   }
>> }
>>
>> pipeline('develop') { // Just for naming
>>   when { branch "develop" }
>>   stages {
>>     stage('Build'){
>>       steps { /* do some build stuff */ }
>>     }
>>     stage('Dev Deploy'){
>>       steps { /* deploy to dev */ }
>>     }
>>   }
>> }
>>
>> pipeline('pull request') { // Just for naming
>>   when { branch "PR-*" }
>>   stages {
>>     stage('Pull Request Build') {
>>       steps { /* do a merge build stuff */ }
>>     }
>>     stage('Pull Request Deploy') {
>>       steps { /* deploy to special PR sandbox */}
>>     }
>>   }
>> }
>>
>> pipeline('feature') { // Just for naming
>>   when { branch != "master || PR-* || develop" } // just do a build for any 
>> 'other' branches, which would then include developer feature branches
>>   stages {
>>     stage('Build') {
>>       steps { /* do some build stuff */ }
>>     }
>>   }
>> }
>>
>>
>> That, to me, is much cleaner. It is very easy to see exactly what each 
>> pipeline is doing.
>> This brings one downside. The stage is repeated.
>> stage('Build') and stage('Dev Deploy') are the same impl, but I have to 
>> write them 2 times.
>> I could create a global library, but then that has 2 other downsides. It is 
>> no longer declarative syntax in the global library, the global library is 
>> loaded external. I have to now go to a whole other file to see that 
>> implementation.
>>   
>> To keep things DRY I would also like to then see the stages treated as a 
>> definition and and implementation.
>> Define the stages external to the pipeline, but pull them into each pipeline.
>>
>> This can optionally be done (like you'll see on the Pull Request stages).
>>
>> Here is what I believe the combination of the two would look like:
>>
>>
>> pipeline('master') { // Just for naming
>>   when { branch "master" }
>>   stages {
>>     stage('Build')
>>     stage('Scan')
>>     stage('Dev Deploy')
>>   }
>> }
>>
>> pipeline('develop') { // Just for naming
>>   when { branch "develop" }
>>     stages {
>>     stage('Build')
>>     stage('Dev Deploy')
>>   }
>> }
>>
>> pipeline('pull request') { // Just for naming
>>   when { branch "PR-*" }
>>   stages {
>>     stage('Pull Request Build') {
>>       steps { /* do a merge build stuff */ }
>>     }
>>     stage('Pull Request Deploy') {
>>       steps { /* deploy to special PR sandbox */}
>>     }
>>   }
>> }
>>
>> pipeline('feature') { // Just for naming
>>   when { branch != "master || PR-* || develop" } // just do a build for any 
>> 'other' branches, which would then include developer feature branches
>>   stages {
>>     stage('Build')
>>   }
>> }
>>
>> /* Stage definitions below */
>> stage('Build'){
>>   steps { /* do some build stuff */ }
>> }
>>
>> stage('Scan'){
>>   steps { /* run static code analysis or other code scanning */}
>> }
>>
>> stage('Dev Deploy'){
>>   steps { /* deploy to dev */ }
>> }
>>
>>
>> Is there a way to do this with the current declarative syntax?
>>
>> If not, what is the best way to get this into the declarative syntax? Open 
>> jira enhancement requests?
>>
>>
>> What we've resorted to in the mean time (which still doesn't solve the DRY 
>> part) is to have a Jenkinsfile that does the if logic and then loads a 
>> specific pipeline (which has its own demons because the load evals the file 
>> immediately and is holding onto a heavyweight executor the whole time).
>>
>>
>> if (env.BRANCH_NAME.startsWith("develop")) {
>>     load 'develop-pipeline.groovy'
>> } else if (env.BRANCH_NAME.startsWith("master")) {
>>     load 'master-pipeline.groovy'
>> } else if (env.BRANCH_NAME.startsWith("PR-")) {
>>     load 'pull-request-pipeline.groovy'
>> } else {
>>     load 'feature-pipeline.groovy'
>> }
>>
>>
>> Thanks,
>>
>> Ken
>>
>>
>>

-- 
You received this message because you are subscribed to the Google Groups 
"Jenkins Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to jenkinsci-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/jenkinsci-users/dcb98b70-e848-4390-ab08-26d4b1e4ce5b%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to