Reactive choice parameter to read from workspace file - jenkins

I am trying to implement active reactive choice parameter .
In reactive parameter basically I am hard coding the different options based on the active parameter
Below is the sample code
if (Target_Environment.equals("Dev01")) {
return ["test_DEV"]
} else if (Target_Environment.equals("Dev02")) {
return ["test3_DEV02","test2_DEV02"]
} else if (Target_Environment.equals("Dev03")) {
return ["test3_DEV03"]
} else if (Target_Environment.equals("Sit03")) {
return ["test3_SIT03"]
}else if (Target_Environment.equals("PPTE")) {
return ["test3_PPTE"]
}
else {
return ["Please Select Target Environment"]
}
Instead hard coding the choices I want to read from a file in jenkins workspace and show the content as the choices , what would be an ideal way to go with that ?
The readFile is not working under return function
I am also trying with extended choice parameter but
there I am passing a property file with filename however how can I pass the property file with if else condition

I'm not 100% sure if this is accurate. But I would expect that you can't read from the job workspace in a parameter like that because workspaces are created after a build (see below error message).
So if you create the job with no previous builds, there will be no workspace file to read from?
Whenever I have seen that parameter type used in the past, they usually read from a file on the server instead of the workspace.
Error message from Jenkins jobs that have no previous builds:
Error: no workspace
A project won't have any workspace until at least one build is performed.
Run a build to have Jenkins create a workspace.

Related

How to check status of specific stage (of previous build) during Jenkins build?

In Jenkins (multi-branch pipeline), I want to be able to find the most recent build in which some stage was successful (e.g. not skipped, not failed). I can easily loop through the previous build and check if the build itself was successful, but I do not know how to check if a given stage was successful for that build.
Collection<String> getChangedFilesSinceLastSuccessfulStageBuild(String... stages) {
def files = new HashSet<String>()
def build = currentBuild
// look at previous build(s) until we find a successful build AND the given stage
// (or leafiest child stage) was also successful (or we reach the beginning of time)
while (build != null && build.result != 'SUCCESS' /* ... unknown here ... */) {
// ... round up the changed files, etc. (easy)
}
// ... more easy things here
return files;
}
This would be called as part of the process to ask the question:
Have any API-related files changed since the last successful API build?
It would be called as (e.g.):
hasApiChanges = getChangedFilesSinceLastSuccessfulStageBuild("Build/Test API")
hasUiChanges = getChangedFilesSinceLastSuccessfulStageBuild("Build/Test API")
(Caveat -- the "Build/Test API" stage is actually a "Build/Test" stage which has parallel stages within it for "API" and "UI". So I really need to find the "API" stage within the "Build/Test" stage.)
Not sure how to find a (nested) stage by name and then check its result.
Alternatively, during that stage I could write some persistent build metadata to the current build, and then check if that metadata exists / is true during subsequent builds. (Not sure how to do this, either.)
PS: I can't seem to find the relevant Javadoc for this (multi branch pipeline) -- would love a link to that.
I ended up implementing this feature without tracking success at the step level. I essentially look back through the recent builds and for any successful build, if it has changes to the "affected" files and before that build there were also changes to the affected files, then the build continues for that set of files (e.g. API).
I also added parameters to the pipeline so that we can override this rule as needed.
Set Up Variables
final String forceApiBuildParam = 'forceApiBuild'
final String skipApiBuildParam = 'skipApiBuild'
final String forceUiBuildParam = 'forceUiBuild'
final String skipUiBuildParam = 'skipUiBuild'
final String apiPathRegex = /^(api\/|build.gradle|checkstyle.xml|settings.gradle|Jenkinsfile).*/
final String uiPathRegex = /^(ui\/|Jenkinsfile).*/
Boolean apiBuildEnabled = params[forceApiBuildParam] == true
? true
: (params[skipApiBuildParam] == true
? false
: hasChangesInPathsSinceLastSuccessFulBuildWithChangesInPaths(verbose, apiPathRegex))
Boolean uiBuildEnabled = params[forceUiBuildParam] == true
? true
: (params[skipUiBuildParam] == true
? false
: hasChangesInPathsSinceLastSuccessFulBuildWithChangesInPaths(verbose, uiPathRegex))
Set Up Parameters
parameters {
booleanParam(name: forceApiBuildParam, defaultValue: false, description: 'Whether or not to force the API to build and deploy.')
booleanParam(name: skipApiBuildParam, defaultValue: false, description: 'Whether or not to skip the API build, even if there are changes.')
booleanParam(name: forceUiBuildParam, defaultValue: false, description: 'Whether or not to force the UI to build and deploy.')
booleanParam(name: skipUiBuildParam, defaultValue: false, description: 'Whether or not to skip the UI build, even if there are changes.')
}
Function to Check for Changes (can likely be simplified)
/**
* Attempts to determine if, for a set of paths given in the form of a regex,
* any files have changed (which match those paths) since the last build which
* was successful and also had changes within those paths.
*
* #param verbose true to include additional logging
* #param pathRegex the regex of the paths to consider
* #return true if paths matching the provided regex have changed since the last
* successful build which also had changes to a file matching the
* regex; false if otherwise or if no successful build yet exist
*/
boolean hasChangesInPathsSinceLastSuccessFulBuildWithChangesInPaths(boolean verbose, String pathRegex) {
Collection<String> allChangedPaths = new HashSet<String>()
RunWrapper build = (RunWrapper) (Object) currentBuild
int buildCount = 0
echo "Checking for changed paths since last successful build for paths matching: ${pathRegex}"
while (build != null) {
if (verbose) echo "Checking build ${build.displayName}..."
WorkflowRun rawBuild = (WorkflowRun) build.rawBuild
String buildHash = rawBuild.getAction(BuildData.class).lastBuiltRevision.sha1String.substring(8)
boolean success = build.result == 'SUCCESS'
if (success) {
echo "Found successful build (${build.displayName}; #${buildHash}) -- ${buildCount} builds ago."
}
else if (verbose) {
echo "Found failed build (${build.displayName}; #${buildHash}) -- ${buildCount} builds ago."
}
for (GitChangeSetList changeLog in (build.changeSets as List<GitChangeSetList>)) {
Collection<String> changedChangeLogPaths = new HashSet<String>()
for (GitChangeSet entry in changeLog) {
if (verbose) echo "Checking commit ${entry.commitId}..."
// if this build is successful, check if the already-stored files match `pathRegex`
// if so, then there have been changes since this successful build
if (success) {
for (String thisBuildPath in entry.affectedPaths) {
if (verbose) echo "Checking path ${thisBuildPath} for commit ${entry.commitId}..."
// check if this file (changed in this build) matches
if (thisBuildPath.matches(pathRegex)) {
if (verbose) echo "Match found!"
// now look through all changed files *before* this build for matches
for (String allBuildsPath in allChangedPaths) {
if (verbose) echo "Checking changed path ${allBuildsPath}..."
// check if this file (which was changed before this build) matches
if (allBuildsPath.matches(pathRegex)) {
echo "Since build ${build.displayName}, path \"${allBuildsPath}\" was changed and matches: ${pathRegex}"
return true
}
}
// if we get here, then we found a successful build matching `pathRegex`, but since then
// there have been no files changed which match `pathRegex` -- so no need to build again
echo "No files changed since build ${build.displayName} matching: ${pathRegex}"
return false
}
}
}
// add the affected paths to the list of changes for the changeset
changedChangeLogPaths.addAll(entry.affectedPaths)
}
// if we get here then the build was not successful or had no files changed whose
// path matches `pathRegex` -- add the files to the list and continue
allChangedPaths.addAll(changedChangeLogPaths)
}
build = build.previousBuild
buildCount++
}
echo "Found no successful builds (checked ${buildCount} builds) with changes to paths matching: ${pathRegex}"
// return true to signify so we can do the initial build
return true;
}
There is one caveat with this approach, and it is that it may build slightly more often than it needs to. Say there is an API change which breaks the build -- not because of an issue in the API code change, but because of an issue in the build script, or deployment of the API (something external to the code change). Then the API will be rebuilt on each subsequent build until another API change comes in.
Build #100 -- API change, broken build
Build #101...#n -- no API change; API rebuilt due to #100 failure -- now successful
Build #102...#n -- no API change; API rebuilt due to #100 failure -- even though #101 was successful since then
Build #n+1 -- API change, rebuild API
Build #n+2 -- no API change; API not rebuilt because #n+1 has API change and was successful
In the example above, the API is rebuilt starting from #101 until the next API change comes in and results in a successful build. I don't think this is possible to fix with this solution unless it is possible to tag, for every build, whether or not the API/UI built successfully. If we could do that, then #102+ would not have been built because we know that the API build was successful on #101, even though no API files were affected during that build.

How to set job order for monitor view in jenkins job DSL groovy script?

I'm creating Build Monitor view with DSL Script, but there is no method in API to set the job order. I can set the order manually in configuration after view is created, but I need to do that within the script.
I'm using https://jenkinsci.github.io/job-dsl-plugin/#path/buildMonitorView as a reference. The only way I suspect it could be possible is configure(Closure) method, but I would still have the same question of how to do it.
My current code:
biuldMonitorView("name-of-the-view") {
jobs {
regex("some regex to include jobs")
recurse()
}
// I would expect something like:
view {
orderByFullName()
}
}
After some trial and error and println calls everywhere I came to this solution:
biuldMonitorView("name-of-the-view") {
jobs { // This part is as before
regex("some regex to include jobs")
recurse()
}
// The solution:
view.remove(view / order)
view / order(class: "com.smartcodeltd.jenkinsci.plugins.buildmonitor.order.ByFullName")
}
Above solution sets job order to "Full name" instead of default "Name".
I found the remove idea at Configure SVN section of job-dsl-plugin, fully qualified names of job order options can be found in the source of jenkins-build-monitor-plugin.
I had the same question today and managed to get Aivaras's proposal to work in the following way:
buildMonitorView("name-of-the-view") {
// Set properties like jobs
jobs {
regex("some regex to include jobs")
recurse()
}
// Directly manipulate the config to set the ordering
configure { view ->
view.remove(view / order)
view / order(class: "com.smartcodeltd.jenkinsci.plugins.buildmonitor.order.ByFullName")
}

How do I use `when` condition on a file's last modified time in Jenkins pipeline syntax

I am creating a Jenkins pipeline, I want certain stage to be triggered only when a particular log file's(log file is located in the server node where all the stages are going to run) last modified date is updated after the initiation of pipeline job, I understand we need to use "When" condition but not really sure how to implement it.
Tried referring some of the pipeline related portals but could not able to find an answer
Can some please help me through this?
Thanks in advance!
To get data about file is quite tricky in a Jenkins pipeline when using the Groovy sandbox since you're not allowed to do new File(...).lastModified. However there is the findFiles step, which basically returns a list of wrapped File objects with a getter for last modified time in millis, so we can use findFiles(glob: "...")[0].lastModified.
The returned array may be empty, so we should rather check on that (see full example below).
The current build start time in millis is accessible via currentBuild.currentBuild.startTimeInMillis.
Now that we git both, we can use them in an expression:
pipeline {
agent any
stages {
stage("create file") {
steps {
touch "testfile.log"
}
}
stage("when file") {
when {
expression {
def files = findFiles(glob: "testfile.log")
files && files[0].lastModified < currentBuild.startTimeInMillis
}
}
steps {
echo "i ran"
}
}
}
}

How to get a dynamic property in Jenkins Groovy DSL

Please read the comments to understand the problem.
job(buildV2PerfTest) {
displayName('Performance Test')
steps {
//I am loading a value into a properties file using the shell command. The name of the variable is callbackUrl
shell('echo "callbackUrl=http://`curl http://169.254.169.254/latest/meta-data/public-hostname`:8080" > env.properties')
//then I add the propeties file to Jenkins properties
environmentVariables {
propertiesFile('env.properties')
}
maven {
goals('-P performance test')
//I want to pass to maven a loaded property here
property('callbackUrl', "${callbackUrl}")
}
}
}
The problem is that when I compile this code it says that the property does not exists. Indeed. It will exist when I trigger the job. I want to know how to reference dynamic properties.
P.S.
The documentation tells how to load the variables, but fails in explain how to access them
The solution was:
property('callbackUrl', "\$callbackUrl")

How can a jenkins plugin get the value of a configuration setting?

I'm creating a Jenkins plugin which is a post-build action. I want the plugin to read the value of the "Root POM" field in the job configuration page. I've been looking through the Javadocs for hudson.model.AbstractBuild and trying getBuildVariables(), getEnvironment() etc. but I don't see anything relevant.
I guess as a last resort I could configure my plugin to prompt the user for the root pom, but the problem is that management wants a plugin that can be deployed automatically on every build without any action on the user's part.
Do you mean you want a plugin to read the configuration of another plugin (the maven one)? If so, I believe you should use something like
Jenkins.getInstance().getDescriptor(RequiredDesc.class);
Your required class might be hudson/maven/Maven3Builder depending on what you are trying to do.
Discussion
Update
I was wrong. This seems to work for me:
if (build instanceof hudson.maven.MavenModuleSetBuild) {
try {
hudson.maven.MavenModuleSetBuild b = (hudson.maven.MavenModuleSetBuild) build;
hudson.EnvVars envVars = b.getEnvironment(listener);
String rootPOM = b.getProject().getRootPOM(envVars);
listener.getLogger().println("rootPOM: " + rootPOM);
} catch (Exception e) {
listener.getLogger().println("ERROR: " + e.getMessage());
}
}

Resources