-
Bug
-
Resolution: Fixed
-
Major
-
None
-
jenkins 2.7.2 LTS
p4-plgin 1.3.8
jdk 1.8 (sun)
OS : redhat 7.2
-
Powered by SuggestiMate
I'm using p4 plugin for several months in freestyle jobs, and I'm moving to 'pipelines' implementation (workflow if you like)
According to this documentation, there are some known limitations :
https://github.com/jenkinsci/p4-plugin/blob/master/SETUP.md
especially :
No access to Environment ${VAR} variables
On the other hand, one line below is written :
Exposed Variables :
- P4_CHANGELIST - current changelist (valid for this to be blank when there are no changes).
Within my pipelines, what I see is that 'P4_CHANGELIST' is not available after a call to p4sync.
Is it expected ?
If so, how do I retrieve the changelist at which the sync is done ? (in order to propagate it to further steps and stages in the pipeline)
- depends on
-
JENKINS-26100 SCM steps should return revision state
-
- Resolved
-
- is blocked by
-
JENKINS-38976 P4_TICKET not exposed for .Groovy scripts
-
- Closed
-
- is related to
-
JENKINS-40885 Environment variables are not exposed in pipeline script
-
- Closed
-
[JENKINS-37584] P4 plugin : P4_CHANGELIST not available in workflow (pipeline)
small additional comment : using the change number to set the build name is also something we enjoy. Doing so from inside a pipeline would be great. (easy to do as long as the change number is available in a variable somehow)
I also need this, without the knowledge of what number was synced i cannot migrate to pipelines.
My pipeline has lots of syncs that happen, and I'd also like to see this kind of behavior. Taken a step further though, I'd like to be able to access detected changelists on a per-sync basis.
If I use the polling option of the checkout method, I'd want to be able to access the list of detected changelists.
Preferably, I'd either be able to specify the target environment variable for each poll, or have it named based on the client name doing the polling.
To give more details of my use case, a typical pipeline script would compile multiple targets on multiple platforms. Each target/platform combination can have different sources. I want to be able to skip target/platform combinations if there are no changes to that specific view.
I'd add another note that in my opinion in the case where a job does multiple syncs/polls, P4_CHANGELIST should contain a deduplicated list of all the changes detected.
Thanks for reminding me. I'll need to update the docs to reflect the changes in 1.4.10.
I fear I need a clue as how to make it work.
just updated the plugin 1.4.10, tried a sync, and println "${P4_CHANGELIST}" did fail
(same result with any flavor of ceho, println, double quote, songle quote, $, culry braces or not ...)
I'm probably missing something
The builtin help has some tips:
http://<YOUR_HOST:8080>/job/<YOUR_JOB>/pipeline-syntax/globals
thank you !
and sorry,really, I wrote my comment before seeing you were about to update the doc. I didn't meant to sound rude or impolite.
The ${env.P4_CHANGELIST} is not working for us but the following is
currentBuild.getRawBuild().getEnvironment()["P4_CHANGELIST"]
Thank you for the details; I had seen your comment in the Swarm change, but hadn't had time to try it. The ${env.P4_CHANGELIST} is used in the unit tests and seems ok, it could be Pipeline versions, but we did test a few combinations. Perhaps it is an assignment or scope issue?
We just tried using the environment variables, but found that if you do multiple checkouts in a single pipeline, the env is locked to the first checkout. Or, to be more precise, if you set any of the P4_* environment variables to any arbitrary value the variable is no longer updated by any future p4 actions.
For example, if you do:
env.P4_CHANGELIST="myOwnValue" checkout scm: [$class: 'PerforceScm', credential: 'XXX', populate: ... (etc, etc) echo env.P4_CHANGELIST
You will see that the output shows 'myOwnValue' and not a changelist number as expected.
This prevents creating a pipeline that does actions on multiple depots in our case.
Jeroen, does this also happen if you do
echo currentBuild.getRawBuild().getEnvironment()["P4_CHANGELIST"]
?
mornejoubert, it happens there too.
What I ran to test it (anonymized and abbreviated):
node { echo "currentBuild P4_CHANGELIST: " + currentBuild.getRawBuild().getEnvironment()["P4_CHANGELIST"] checkout poll: false, scm: [$class: 'PerforceScm', credential: '123XYZ', populate: [...], workspace: [...] echo "currentBuild P4_CHANGELIST: " + currentBuild.getRawBuild().getEnvironment()["P4_CHANGELIST"] } node { echo "currentBuild P4_CHANGELIST: " + currentBuild.getRawBuild().getEnvironment()["P4_CHANGELIST"] checkout poll: false, scm: [$class: 'PerforceScm', credential: '123XYZ', populate: [...], workspace: [...] echo "currentBuild P4_CHANGELIST: " + currentBuild.getRawBuild().getEnvironment()["P4_CHANGELIST"] }
In the example above I use two different workspaces (on different depots, but the same perforce server).
The relevant output I see is:
currentBuild P4_CHANGELIST: null ... P4 Task: syncing files at change: 2498817 ... currentBuild P4_CHANGELIST: 2498817 currentBuild P4_CHANGELIST: 2498817 ... P4 Task: syncing files at change: 2465266 ... currentBuild P4_CHANGELIST: 2498817
So even for that access method, the environment is locked to the first value it 'sees'.
I "think" I can see the problem.
The extention that Paul added to set the env has
@Override public void buildEnvironmentFor(Run run, EnvVars env, TaskListener listener) throws IOException, InterruptedException { TagAction tagAction = run.getAction(TagAction.class); if(tagAction == null) { return; } // Set P4_CHANGELIST value if (tagAction.getBuildChange() != null) { String change = tagAction.getBuildChange().toString(); env.put("P4_CHANGELIST", change); }
From http://javadoc.jenkins-ci.org/hudson/model/Actionable.html#getAction(java.lang.Class)
public <T extends Action> T getAction(Class<T> type)
Gets the action first instance to be found of a specified type that contributed to this build.
So my guess is that the code get the first P4Action every time, and I think we want the last ?
There is an API to pull all Actions of a particular type
public <T extends Action> List<T> getActions(Class<T> type)
I would suggest we try that and see if it works if we used the last one in the list.
Yes I think that is the issue, another user spotted the use of getAction vs getActions in a different area:
https://github.com/jenkinsci/p4-plugin/pull/32
Code changed in jenkins
User: Paul Allen
Path:
SETUP.md
http://jenkins-ci.org/commit/p4-plugin/a26323ecd4be72301a180bc03ae84d7f86c4d7ae
Log:
Update documentation for new P4_CHANGELIST behavior
(karl_wirth)
JENKINS-37584
Does this really work for anyone ?
Personally with p4 plugin v1.4.12 I get a 'null' when trying this :
stage ('sync'){ node ('myNode'){ def p4ws='myMaster-${JOB_NAME}' def p4vmap='//depot/'+BRANCH+'/.ci/jenkins/... //'+p4ws+'/...' def populateClassName = 'ForceCleanImpl' checkout([$class: 'PerforceScm', credential: 'PERFORCE-READONLY', populate: [$class: populateClassName, have: true, quiet: true], workspace: [ $class: 'ManualWorkspaceImpl', name: p4ws, spec: [view: p4vmap, allwrite: false] ] ]) println env.P4_CHANGELIST } }
P4 Task: saving built changes. ... p4 client -o ci-mx-test-p4web-testNewP4 + ... p4 info + ... done [Pipeline] echo null
This is the example that works for me on Jenkins 2.19.4 in a 'pipeline' job:
node { stage 'Stage 1' echo 'Hello World 1' p4sync charset: 'none', credential: 'localhost', depotPath: '//depot/pipelinetrig', populate: [ $class: 'AutoCleanImpl', delete: true, modtime: false, parallel: [ enable: false, minbytes: '1024', minfiles: '1', path: '/usr/local/bin/p4', threads: '4' ], pin: '', quiet: false, replace: true ] println env.P4_CHANGELIST }
This returns:
...cut... P4 Task: saving built changes. Found last change 715 on syncID jenkins-NODE_NAME-Localhost-Pipeline-Triggered ... p4 client -o jenkins-master-Localhost-Pipeline-Triggered + ... p4 info + ... p4 changes //jenkins-master-Localhost-Pipeline-Triggered/...@715,716 + ... p4 changes -l -m1 @716 + ... p4 user -o super + ... p4 describe -s 716 + ... p4 fixes -c716 + ... done [Pipeline] echo 716
Note that my output had a lot more information. Was there a built changelist reported on the status page?
I copy / pasted you snippet, still getting 'null'. Changed the sync'd path to be sure somehting was done,
tried again, I have lots of output too... but still null . What do you mean by 'status page' ?
P4 Task: reverting all pending and shelved revisions. ... p4 revert /data/jenkins/slave/workspace/testNewP4/... + ... rm [abandoned files] duration: (11ms) P4 Task: cleaning workspace to match have list. ... p4 reconcile -w -f /data/jenkins/slave/workspace/testNewP4/... + duration: (18ms) P4 Task: syncing files at change: 3800009 ... p4 sync /data/jenkins/slave/workspace/testNewP4/...@3800009 + duration: (147ms) P4 Task: saving built changes. Found last change 3769363 on syncID jenkins-NODE_NAME-testNewP4 ... p4 client -o jenkins-master-testNewP4 + ... p4 info + ... p4 changes //jenkins-master-testNewP4/...@3769363,3800009 + ... p4 user -o xxxxxx+ ... p4 describe -s 3800009 + ... p4 fixes -c3800009 + ... p4 changes -l -m1 @3799947 + ... p4 user -o xxxxxx+ ... p4 describe -s 3799947 + ... p4 fixes -c3799947 + ... p4 changes -l -m1 @3798904 + ------%< snip snip ----------%<------ ... done [Pipeline] echo null [Pipeline] } [Pipeline] // node
It's the summary page of the job. Usually 'Status' on the left hand side. However based on the output above I think the answer is 'yes' and it includes '3798904'.
Can you please post your Jenkins server version, OS the build job and server are on and how you are triggering the build (for example Perforce trigger, polling, Swarm review) and I'll try and reproduce the problem when I return to the office after the holiday break (if it hasn't been solved by others in the meantime).
I'm running Jenkins 2.19.4 on a redhat 7.2 and java8
The slaves on which I ran the job are either redhat 7.2, or 6.4, no idea if it could affect the result.
(yet note that the slave is not on the same host as the master)
build is triggered manually for now
Update
using 2.32.1, p4 plugin 1.4.12, all on a windows laptop, fresh install, still doesn't work
here's the pipeline snippet and the output
node('master'){ println env.P4_CHANGELIST def populateClassName = 'SyncOnlyImpl' def p4ws='squalou-test-hell' def viewMapping='''//depot/project/pom.xml //squalou-test-hell/project/pom.xml''' checkout([$class: 'PerforceScm', credential: 'PERFORCE-READONLY', populate: [$class: populateClassName, have: false, quiet: true], workspace: [ $class: 'ManualWorkspaceImpl', name: p4ws, spec: [view: viewMapping] ] ]) println env.P4_CHANGELIST }
output
Running on master in C:\Users\squalou\.jenkins\workspace\p4Hell [Pipeline] { [Pipeline] echo null [Pipeline] checkout ... p4 client -o squalou-test-hell + ... p4 info + ... p4 client -o squalou-test-hell + ... p4 client -i + ... client: squalou-test-hell ... p4 client -o squalou-test-hell + ... p4 info + ... p4 counter change + ... p4 changes -m1 -ssubmitted //squalou-test-hell/... + Change 3832207 on 2017/01/06 by someone@something ' tadada' Building on Node: master ... p4 client -o squalou-test-hell + ... p4 info + P4 Task: establishing connection. ... server: perforce:1666 ... node: SQUALOU P4 Task: syncing files at change: 3832207 ... p4 sync -p -q C:\Users\squalou\.jenkins\workspace\p4Hell/...@3832207 + duration: (209ms) P4 Task: saving built changes. Found last change 3832207 on syncID squalou-test-hell ... p4 client -o squalou-test-hell + ... p4 info + ... p4 client -o squalou-test-hell + ... p4 info + ... done [Pipeline] echo null [Pipeline] } [Pipeline] // node [Pipeline] End of Pipeline
Try removing the first
println env.P4_CHANGELIST
as this is defining a 'null' entry, before the variable is used and the environment is not overriding it.
This looks like a Jenkins Core bug as I just extend the environment.
I tried, still failing.
(to be honest I first tried without, and added it after to check)
I also tried several 'p4 sync' implementation, populate have list or not, using groovy sandbox or not : same result.
I tried on another machine : it works when removing the println.
It's weird. On the one it failed, there must be something in the way.
I've tried something funny before the checkout
println env.JOB_NAME
... makes it fail again.
I even tried to iterate over env.getEnvironnment() before the checkout, to be sure not to call a null thing : nope, P4_whetever is not filled after the checkout.
Really weird.
env.getOveridenEnvironnment itself is always empty.
My problem is that because of this : access to P4 variables are not reliable, so not usable :
- if I define workspace using env.JOB_NAME (or any env) : it make things fail ! it's a very common usecase
- if I chain 2 syncs in a pipeline, and between I use the P4_CHANGELIST : I'm probably doomed
- if I call whatever env variable before I sync, I' doomed
- if a shared lib I use do use any env variable before I sync : doomed again
note that it happens with all P4 env variables, not only p4_CHANGELIST
Out of curiosity, are you using an alpine or a libc distribution? docker or native?
I am reproducing the issue with alpine/docker.
The currentBuild.getRawBuild().getEnvironment()["P4_PORT" / "P4_USER" / "P4_CHANGELIST"] workaround works for me and allowed me to setup a fairly complex job with several external p4 operations done in python scripts.
Hey,
GREAT workaround ! works for me too.
for your curiosity : I'l using a plain war of jenkins inside a tomcat-8 on my windows 7 laptop, and also some init.d-style service installations under redhat
For what it's worth, I just hit this.
I get null unless I use the rawBuild workaround.
Jenkins LTS ver. 2.32.1
p4-plugin 1.4.13
Jenkins is running on Debian 8.4, the slave that Jenkins chose for the job was Debian 8.6
Same behaviour when triggered by polling or manually.
Hi,
I'm experiencing the same behavior as the OP (P4_CHANGELIST is not defined).
When I tried to use the suggested workaround: currentBuild.getRawBuild().getEnvironment()["P4_CHANGELIST"], I got the following error message:
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use method org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper getRawBuild
I checkout the pipeline script with SCM.
Any other way I can get this working in the meantime?
liranv, I'm not sure how to disable the groovy sandbox for scripts loaded from SCM, if you can, but you can alternatively add a function to the pipeline shared library to call the function for you.
e.g.
https://jenkins.io/doc/book/pipeline/shared-libraries/#defining-steps
// vars/getEnvironmentWrapper.groovy def call(String variableName) { return currentBuild.rawBuild.getEnvironment()[variableName] }
rawBuild isn't serializable, but you should get away with that in a standalone function. I can't say for sure because I have my helper function written into a larger class.
You can simply approve the script, can't you ? it's a one time click per jenkins instance, so if you have only a few, it's sort of ok.
OR : the solution I adopted is to use plugin that allows all scripts. ( permissive-security-script plugin )It has a minor drawback : it's too verbose when it logs that it allowed a script. But works fine.
squalou, It seems that scripts that are being checked out via SCM cannot be approved (Run out of sandbox).
I my case I can, and the plugin works too.
I'm running 2.32.2 LTS with 'stable' plugins up to date, and has been working like that (with scripts from SCM too) for months now.
I hope it will stay the same in next releases ! (I stay on LTS, so I don't know for weekly)
p4karl p4paul I think I managed to get a simple reproducible example of this.
This simple declarative pipeline will work as long as I don't use the environment block.
pipeline { agent any //environment { //firstAccess = "I crash the build" //} options { buildDiscarder(logRotator(numToKeepStr: '10')) skipDefaultCheckout() } stages { stage("Checkout") { steps { p4sync credential: 'p4mw-credential', depotPath: '//mydepot/main', populate: [$class: 'CheckOnlyImpl', force: false, have: false, modtime: false, quiet: false] } } stage("Echo world") { steps { echo "Hello world! Built on $P4_CHANGELIST curly: ${P4_CHANGELIST} env ${env.P4_CHANGELIST} " echo " firstAccess = ${env.firstAccess}" sh 'echo P4_CHANGELIST bash = $P4_CHANGELIST' } } } }
This gives output:
[Pipeline] stage [Pipeline] { (Echo world) [Pipeline] echo Hello world! Built on 2075507 curly: 2075507 env 2075507 [Pipeline] echo firstAccess = null [Pipeline] sh [p4test-declarative] Running shell script + echo P4_CHANGELIST bash = 2075507 P4_CHANGELIST bash = 2075507 [Pipeline] } [Pipeline] // stage [Pipeline] } [Pipeline] // node [Pipeline] End of Pipeline Finished: SUCCESS
If I add the commented environment block, then I get:
[Pipeline] stage [Pipeline] { (Echo world) [Pipeline] } [Pipeline] // stage [Pipeline] } [Pipeline] // withEnv [Pipeline] } [Pipeline] // node [Pipeline] End of Pipeline groovy.lang.MissingPropertyException: No such property: P4_CHANGELIST for class: WorkflowScript at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:53) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.getProperty(ScriptBytecodeAdapter.java:458) at org.kohsuke.groovy.sandbox.impl.Checker$4.call(Checker.java:243) at org.kohsuke.groovy.sandbox.GroovyInterceptor.onGetProperty(GroovyInterceptor.java:52) at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onGetProperty(SandboxInterceptor.java:308) at org.kohsuke.groovy.sandbox.impl.Checker$4.call(Checker.java:241) at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:238) at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:221) at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:221) at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:221) at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:221) at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.getProperty(SandboxInvoker.java:28) at com.cloudbees.groovy.cps.impl.PropertyAccessBlock.rawGet(PropertyAccessBlock.java:20) at WorkflowScript.run(WorkflowScript:18)
If you feel that this is not a p4-plugin error, could you please file and link an issue in whatever pipeline plugin that needs to fix this?
Code changed in jenkins
User: Paul Allen
Path:
src/main/java/org/jenkinsci/plugins/p4/PerforceScm.java
src/test/java/org/jenkinsci/plugins/p4/client/WorkflowTest.java
http://jenkins-ci.org/commit/p4-plugin/0ef5086068dd34198e20839544fe87761d252756
Log:
Set SCM Environment for Jenkins 2.60+
p4paul Would you care to comment on how this was fixed? Do we have to upgrade to 2.60+ for this to work? Do we need other syntax?
Jenkins added a fix in workflow-scm-step 2.6 and requires core 2.60+
https://wiki.jenkins.io/display/JENKINS/Pipeline+SCM+Step+Plugin
This is called 'Pipeline: SCM Step' in Installed plugins list.
p4paul Ok, so now I have updated my jenkins to 2.60.3 and p4 plugin 1.7.4.
I'm still not seeing the environment variables being populated. I thought this was supposed to happen. Otherwise https://github.com/jenkinsci/p4-plugin/blob/master/SETUP.md#exposed-variables should probably be updated.
I also tested with:
def p4vars = p4sync charset: 'none', credential: 'cred', populate: forceClean(have: false, parallel: [enable: false, minbytes: '1024', minfiles: '1', threads: '4'], pin: 'now', quiet: true), source: depotSource('//depot/stuff/...')
In this case, p4vars.P4_CHANGELIST returns 'now', previously P4_CHANGELIST was expanded to the actual numerical changelist.
I can't find the proper way to get the actual P4_CHANGELIST that was synced now.
Please do not use pin: 'now' this will override the change number and break polling. Just leave pin as an empty string ('') to build the latest; for example:
def env = p4sync credential:'phooey1666', format:"jenkins-${NODE_NAME}-${JOB_NAME}", stream:'//streams/st1-main', populate:[$class:'AutoCleanImpl', delete:true, have:true, modtime:false, quiet:false, pin:''] echo env.P4_CHANGELIST echo env.P4_CLIENT
output:
[Pipeline] echo 11593 [Pipeline] echo jenkins-master-dsl1
p4paul Ok, thanks for the feedback.
Got the scripts working again after this change.
Problem with some of the latest changes is that they break compatibility with previous scripts and thus we need to change Jenkinsfile on multiple old branches just to get them to build again. Also updating the steps references (https://jenkins.io/doc/pipeline/steps/p4/), especially p4sync would be appreciated.
Currently there are a lot of similar options that are not documented properly see e.g. p4sync arguments depotPath vs source. Was 'depotPath' deprecated etc... Also some kind of semantic versioning scheme would be nice as my Jenkinsfile scripts tend to break even on one of these 1.7.0 -> 1.7.4 upgrades.
Thank you for letting me know about the URL (https://jenkins.io/doc/pipeline/steps/p4/). Jenkins created this based on the code, but I am not sure how they update it? I agree it is confusing to read, I normally use the 'Pipeline Syntax' snippet generator and help bubbles to workout what options are available. The older depotPath vs source should be backwards compatible.
For example 3 ways to define a stream on p4sync:
stream: '//streams/foo' source: [$class: 'StreamSource', stream: '//streams/foo'] source: streamSource('//streams/foo')
I think the docs at the URL is generated from the same stuff as 'Pipeline syntax'. Basically there some options that are undocumented in 'Pipeline syntax' too. But the jenkins steps references could definitely be made more readable. Couldn't find a jira for that though... might file one tomorrow in jenkins-io.
Our previous job looked essentially like:
def p4Pin = 'now' p4sync credential: 'cred', depotPath: env.P4_DEPOT, populate: [$class: 'ForceCleanImpl', quiet: true], pin: p4Pin
P4_DEPOT ="//depot/releases/3.4.x" was set in the job as a environment variable.
The depotPath had no ending '/..' before and after the upgrade this started failing due to p4-plugin generating the incorrect view client view:
View: //depot/releases/3.4.x //jenkins-somenodename-thejobname/3.4.x
I.e. it started mapping the wanted depot dir to a client sub dir instead of the root of the client sometime between 1.7.0 and 1.7.4.
This doesn't work for me at all with the latest p4 plugin. (jenkins version: 2.68 which is higher than the 2.6x mentioned above) All other plugins up to date. I just get null.
I'm not using 'populate' like above though, i'm passing a workspace and spec:
def p4env = p4sync poll: true, credential: '', workspace: [$class: 'org.jenkinsci.plugins.p4.workspace.ManualWorkspaceImpl', charset: "", pinHost: false, name: client, spec: [$class: 'org.jenkinsci.plugins.p4.workspace.WorkspaceSpec', view: view] ] echo "Sync p4changelist:" echo p4env.P4_CHANGELIST
also tried this as above:
p4env = p4sync poll:true, credential:'e6b4d2aa-df16-4ab5-aa9d-e8a149f1096f', format: "<redacted>-${NODE_NAME}-${JOB_NAME}", stream:'<mystream>', populate:[$class:'AutoCleanImpl', delete:true, have:true, modtime:false, quiet:false, pin:''] echo "Sync p4changelist:" echo p4env.P4_CHANGELIST
results for both:
[Pipeline] echo
Sync p4changelist:
[Pipeline] echo
null
Code changed in jenkins
User: Paul Allen
Path:
src/main/java/org/jenkinsci/plugins/p4/build/P4EnvironmentContributor.java
http://jenkins-ci.org/commit/p4-plugin/65279c9d1158fa247e406d6146ef8395d39906c6
Log:
Copy environment map to EnvVars.
You can download the latest HPI build here: https://ci.jenkins.io/job/Plugins/job/p4-plugin/job/master/100/
Additionally : setting the 'change' variable in a pipeline seem to have no effect either.
I've tried to set the 'pin' value to a changelist, and apparently it works but I'm not sure it's a recommended use.
So to be more precise on the needs here :