VSTS Release multi-line variable - tfs

I have task in VSTS release management that deletes files. I want to have the Contents come from a variable. I have created a variable but I can't figure out how to create a multi-line variable. So for example a variable that deletes the three types of files:
Variable Name= ExcludeFiles
Variable Value= "Lib" "bin\*.pdb" "bin\*.dll.config"

It's possible to use a variable with multiple logical lines as input to a multi-line task parameter, but it may not work for every task. This approach is tested on VSTS using the VS Test 2.x task, which I'll use as an example below. I expect it will work for most of the Microsoft-provided tasks.
Background
Tasks define a set of parameters via a JSON file. Each parameter has an internal name in addition to the display name shown in the UI. It's possible to see the internal parameter name using the "Link settings" button on a task (or by finding the task's source code).
In the "Link settings" dialog, the VS Test 2.x task has a "Setting to link" called "Test assemblies", which is a multi-line string. Looking at "Process parameter to link to this setting", we see the value "Parameters.testAssemblyVer2". testAssemblyVer2 is the name of the internal parameter.
When a task executes, it needs to obtain values for its parameters. Most tasks do this by searching the current environment variables for anything starting with "INPUT_". In the case of testAssemblyVer2, the task will look for an environment variable named INPUT_TESTASSEMBLYVER2.
Just before the task executes, we can turn a delimited variable value into an encoded multi-line value, and write it into the environment variable where it's picked up by the task.
Solution
First, define a variable, "Custom.TestAssemblies" with a semicolon-delimited value **\$(BuildConfiguration)\*.tests.dll;!**\obj\**. The semicolon will become the line split.
Next, add a PowerShell task to the build process just before the VS Test task. Configure it as an Inline script with one Argument "$(Custom.TestAssemblies)". Here, the double quotes are critical.
The inline script looks like this:
Param([String]$toMultiLine)
$newlineDelimited = $toMultiLine -replace ';', "%0D%0A"
Write-Host "##vso[task.setvariable variable=INPUT_TESTASSEMBLYVER2]$newlineDelimited"
That's it! The delimiters in the variable value are converted to URL-encoded CR/LF's, and the agent is instructed to update INPUT_TESTASSEMBLYVER2 with that value. The task picks up the value and parses it for '\n', which matches the embedded %0D%0A's.
Summary
Pick a delimiter like ; and use it to divide the parts of your variable value
Obtain the task's internal parameter name using "Link settings"
Insert a PowerShell task just before the target task and insert the code above, substituting the correct variable and task parameter names
If you set the variable system.debug to true, you'll generally see the various INPUT_ parameters and some of the parsing in the trace output. It depends on the implementation of the specific task.
This solution should work equally well for Build and Release sequences.

Multi-line variable is not supported, I submit a user voice here: Multiple lines variable in Build and Release.
Based on the source code of Delete Files task, it splits contents value by ‘\n’, but based on my test, add ‘\n’ to variable isn’t working (e.g. t1.txt \n t2.txt or t1.txt\nt2.txt).
You can custom build/release task per to the source code of Delete Files task or to do it with your logical and execute it through PowerShell/Command Line task.

Bash solution:
emailbody=$(echo "$output" | sed ':a;N;$!ba;s/\n/%0D%0A/g')

The PowerShell solution from Thomas F. Abraham solved my problem. This modification makes it a bit simpler, no input parameter needed:
$newline = "%0D%0A `t"
Write-Host "##vso[task.setvariable variable=LineBreak]$newline"
I also added a tab character so my next line would be indented. Then just refer to the variable $(LineBreak) where ever you want it.

Solution from Thomas also helped point us in right direction.
No issue loading multiline certificates/keys using "bash" task in azure pipeline with,
export CERTIFICATE=$(echo "$(CERTIFICATE_BASE64)" | base64 -d -w 0)
echo "##vso[task.setvariable variable=CERTIFICATE;]$(echo $CERTIFICATE)"
However trying same using "powershell" task in azure pipeline didn't work with,
$CERTIFICATE = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String("$(CERTIFICATE_BASE64)"))
$CERTIFICATE.Replace("`n","`r`n")
Write-Output "##vso[task.setvariable variable=CERTIFICATE;]$CERTIFICATE"
Swapping in the following did work,
$CERTIFICATE.Replace("`n","%0D%0A")

Solution from "Thomas F. Abraham" helped to write a yml template for the VsTest task:
#Note that it is tricky to specify the multiline-string for the VSTest task
#see 'https://github.com/MicrosoftDocs/azure-devops-docs/issues/1580'
#see 'https://stackoverflow.com/questions/44464976/vsts-release-multi-line-variable' for the solution used in this template
#we set the environment variable 'INPUT_TESTASSEMBLYVER2' instead of setting the input 'testAssemblyVer2' for the task !!!
parameters:
- name: testAssemblies
type: string
default: '**\*test*.dll,!**\*TestAdapter.dll,!**\obj\**'
- name: searchFolder
type: string
default: $(Build.Repository.Name)
- name: codeCoverageEnabled
type: boolean
default: false
steps:
- script: |
echo Paramater testAssemblies: ${{ parameters.testAssemblies }}
echo Paramater searchFolder: ${{ parameters.searchFolder }}
echo Paramater codeCoverageEnabled: ${{ parameters.codeCoverageEnabled }}
displayName: 'Parameters for VSTest'
- powershell: |
$newline = "%0D%0A"
$newlineDelimitedTestAssemblies = '${{ parameters.testAssemblies }}' -replace ',', $newline
Write-Host "##vso[task.setvariable variable=INPUT_TESTASSEMBLYVER2]$newlineDelimitedTestAssemblies"
displayName: 'Set INPUT_TESTASSEMBLYVER2 for VSTest task'
- task: VSTest#2
inputs:
testSelector: 'testAssemblies'
searchFolder: '${{ parameters.searchFolder }}'
vstestLocationMethod: 'location'
vstestLocation: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\Extensions\TestPlatform'
codeCoverageEnabled: ${{ parameters.codeCoverageEnabled }}
displayName: 'Run VSTest VS2022'

Related

How to pass pipeline variables to post build gerrit message?

I have a Pylint running in a Jenkins pipeline. To implement it, I used Gerrit trigger plugin and Next Generation Warnings plugin. Everything is working as expected - Jenkins is joining the review, checks change with pylint and generates report.
Now, I'd like to post pylint score in a custom "Build successful" message. I wanted to pass the pylint score to a environment variable and use it in dedicated window for Gerrit plugin message.
Unfortunately no matter what I try, I cannot pass any "new" variable to the message. Passing parameters embedded in pipeline works (e.g. patchset number).
I created new environment variable in Configure Jenkins menu, tried exporting to shell, writing to it (via $VAR and env. syntax) but nothing works - that is, build message displays raw string like $VAR instead of what variable contains.
What should I do to pass local pylint score (distinct for every pipeline occurence) to the custom build message for Gerrit?
I don't think the custom message can be used for this. This is just supposed to be a static message.
They way I do this is to use the SSH command to perform the review. You can also achieve the same using the REST API.
First I run my linting and white space checking script that will generate a json file with the information I would like to pass to Gerrit. Next I send it to Gerrit using SSH. See below my pipeline script and an example json file.
As a bonus I have added the robot comments. This will now show up in your review as a remark from Jenkins that line 8 of my Jenkins file has a trailing white space. You can easily replace this with your lint result of you like or just ignore it and only put the message. It is easier to use a json file as it will make it easier to create multi line messages
node('master') {
sh """
cat lint_change.json | ssh -p ${env.GERRIT_PORT} ${env.GERRIT_HOST} gerrit review ${env.GERRIT_PATCHSET_REVISION} --json
"""
}
Example json file:
{
"labels": {
"Code-Style": "-1"
},
"message": "Lint Bot Review\nLint Results:\n Errors: 0\n Warnings: 0\n\nWhitespace results:\n Errors: 1",
"robot_comments": {
"Jenkinsfile": [
{
"robot_id": "lint-bot",
"line": "8",
"message": "trailing whitespace."
}
]
}
}
Alternatively, you may want to look at a new gerrit-code-review-plugin that should make this things even easier. However, I have not tried this yet.

file parameter in declarative pipeline

I am developing declarative pipeline and want to use file parameter to read its content, but its not working as expected
parameters{
file(fileLocation:'list.txt', description:'contains list of projects to be build')
}
I am getting following error
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 12: Invalid parameter "fileLocation", did you mean "description"? # line 12, column 14.
file(fileLocation:'release-list.txt', description:'contains list of projects to be build')
Following is another option mentioned for basic step plugin
readFile: Read file from workspace
Reads a file from a relative path (with root in current directory, usually workspace) and returns its content as a plain string.
file
Relative ( /-separated) path to file within a workspace to read.
Type: String
encoding (optional)
Type: String
its working in script step like
def myfile = readFile('list.txt')
echo "${myfile}"
But how to use it directly in declarative script as we used other basic steps like dir??
The correct arguments for the file parameter are name and description. So it should be:
file(name:'list.txt', description:'contains list of projects to be build')
However there's an open jenkins issue dating back from 2015 about the file parameter not working for pipelines, so I don't think even this will solve your issue. https://issues.jenkins-ci.org/browse/JENKINS-27413
Following syntax is working
parameters{
file name:'list.txt', description:'contains list of projects to be build'
}
But fileLocation parameter is not acceptable still.
Below syntax is available in Jenkins2 Up & Running book but its not working
parameters{
file(fileLocation:'list.txt', description:'contains list of projects to be build')
}
Till outstanding issues gets fixed, I believe we may have to stick to freestyle mode & handle things either in downstream pipeline job or within same job leveraging needy plugin feature.
Here is my attempt which looks to work file irrespective (yes supports Binaries as well) types : https://i.stack.imgur.com/vH7mQ.png
${list.txt} will point to right file in your case..
Take a look at the plug-in https://plugins.jenkins.io/file-parameters/.
This plug-in adds support for file parameters in your Jenkinsfile: https://plugins.jenkins.io/file-parameters/#plugin-content-usage-in-declarative-pipeline
parameters {
base64File 'small'
stashedFile 'large'
}
https://github.com/jenkinsci/file-parameters-plugin

Is possible to change the value of a variable during execution of a release in TFS 2017

In TFS 2017, when a release definition is created a set of custom variables can be created too.
In the scope of an Agent, Is possible to change the value of one variable?
I tried with an inline powershell script:
$env:MyVariable = "changed value"
also try with :
[Environment]::SetEnvironmentVariable("MyVariable ", "changed value.", "User")
without success.
You could use the Logging command to change the custom variable's value.
In your PowerShell script file(script1.ps1), write:
$NewVersion = "NewValue"
Write-Host ("##vso[task.setvariable variable=customVariable;]$NewVersion")
Then add a Powershell script to run this file.
And you could add another Powershell script file(script2.ps1) to output the custom value. Run this file after script1 to check if the value has been changed successfully.
Here is a similar question: How to change a tfs build variable in script
Did you try Write-Host?
Write-host $env:OutputVar
Can't check myself now, but you can take a look here for detail.

How to get build time stamp from Jenkins build variables?

How can I get build time stamp of the latest build from Jenkins?
I want to insert this value in the Email subject in post build actions.
Build Timestamp Plugin will be the Best Answer to get the TIMESTAMPS in the Build process.
Follow the below Simple steps to get the "BUILD_TIMESTAMP" variable enabled.
STEP 1:
Manage Jenkins -> Plugin Manager -> Installed...
Search for "Build Timestamp Plugin".
Install with or without Restart.
STEP 2:
Manage Jenkins -> Configure System.
Search for 'Build Timestamp' section, then Enable the CHECKBOX.
Select the TIMEZONE, TIME format you want to setup with..Save the Page.
USAGE:
When Configuring the Build with ANT or MAVEN,
Please declare a Global variable as,
E.G. btime=${BUILD_TIMESTAMP}
(use this in your Properties box in ANT or MAVEN Build Section)
use 'btime' in your Code to any String Variables etc..
NOTE: This changed in Jenkins 1.597, Please see here for more info regarding the migration
You should be able to view all the global environment variables that are available during the build by navigating to https://<your-jenkins>/env-vars.html.
Replace https://<your-jenkins>/ with the URL you use to get to Jenkins webpage (for example, it could be http://localhost:8080/env-vars.html).
One of the environment variables is :
BUILD_ID
The current build id, such as "2005-08-22_23-59-59" (YYYY-MM-DD_hh-mm-ss)
If you use jenkins editable email notification, you should be able to use ${ENV, var="BUILD_ID"} in the subject line of your email.
One way this can be done is using shell script in global environment section, here, I am using UNIX timestamp but you can use any shell script syntax compatible time format:
pipeline {
agent any
environment {
def BUILDVERSION = sh(script: "echo `date +%s`", returnStdout: true).trim()
}
stages {
stage("Awesome Stage") {
steps {
echo "Current build version :: $BUILDVERSION"
}
}
}
}
Try use Build Timestamp Plugin and use BUILD_TIMESTAMP variable.
Generate environment variables from script (Unix script) :
echo "BUILD_DATE=$(date +%F-%T)"
I know its late replying to this question, but I have recently found a better solution to this problem without installing any plugin. We can create a formatted version number and can then use the variable created to display the build date/time.
Steps to create: Build Environment --> Create a formatted version number:
Environment Variable Name: BUILD_DATE
Version Number Format String: ${BUILD_DATE_FORMATTED}
thats it. Just use the variable created above in the email subject line as ${ENV, var="BUILD_DATE"} and you will get the date/time of the current build.
You can use the Jenkins object to fetch the start time directly
Jenkins.getInstance().getItemByFullName(<your_job_name>).getBuildByNumber(<your_build_number>).getTime()
also answered it here:
https://stackoverflow.com/a/63074829/1968948
BUILD_ID used to provide this information but they changed it to provide the Build Number since Jenkins 1.597. Refer this for more information.
You can achieve this using the Build Time Stamp plugin as pointed out in the other answers.
However, if you are not allowed or not willing to use a plugin, follow the below method:
def BUILD_TIMESTAMP = null
withCredentials([usernamePassword(credentialsId: 'JenkinsCredentials', passwordVariable: 'JENKINS_PASSWORD', usernameVariable: 'JENKINS_USERNAME')]) {
sh(script: "curl https://${JENKINS_USERNAME}:${JENKINS_PASSWORD}#<JENKINS_URL>/job/<JOB_NAME>/lastBuild/buildTimestamp", returnStdout: true).trim();
}
println BUILD_TIMESTAMP
This might seem a bit of overkill but manages to get the job done.
The credentials for accessing your Jenkins should be added and the id needs to be passed in the withCredentials statement, in place of 'JenkinsCredentials'. Feel free to omit that step if your Jenkins doesn't use authentication.
This answer below shows another method using "regexp feature of the Description Setter Plugin" which solved my problem as I could not install new plugins on Jenkins due to permission issues:
Use build timestamp in setting build description Jenkins
If you want add a timestamp to every request from browser to jenkins server.
You can refer to the jenkins crumb issuer mechanism, and you can hack the /scripts/hudson-behavior.js add modify here. so it will transform a timestamp to server.
/**
* Puts a hidden input field to the form so that the form submission will have the crumb value
*/
appendToForm : function(form) {
// add here. ..... you code
if(this.fieldName==null) return; // noop
var div = document.createElement("div");
div.innerHTML = "<input type=hidden name='"+this.fieldName+"' value='"+this.value+"'>";
form.appendChild(div);
}

How to specify a value for a Jenkins environment variable that contains a space

I am trying to specify a value for a Jenkins environment variable (as created on the Manage Jenkins -> Configure System screen, under the heading "Global properties") which contains a space. I want to use this environment variable in an Execute Shell build step. The option that I need to appear in the command line in the build step is:
--platform="Windows 7"
The syntax I am using on the command line is --platform=${VARIABLE_NAME}
No matter how I attempt to format it, Jenkins seems to reformat it so that it is treated as two values. I have tried:
Windows 7
"Windows 7"
'Windows 7'
Windows\ 7
The corresponding results, when output during the Execute Shell build step have been:
--platform=Windows 7
'--platform="Windows' '7"'
'--platform='\''Windows' '7'\'''
--platform=Windows/ 7
I have also tried changing my command line syntax to --platform='${VARIABLE_NAME}' as well as '--platform=${VARIABLE_NAME}', but in each of those cases the ${VARIABLE_NAME} is not resolved at all and just appears as ${VARIABLE_NAME} on the resulting command.
I am hoping there is a way to make this work. Any suggestions are most appreciated.
You should be able to use spaces without any special characters in the global properties section.
For example, I set a variable "THIS_VAL" to have the value "HAS SPACES".
My test build job was the following:
#!/bin/bash
set +v
echo ${THIS_VAL}
echo "${THIS_VAL}"
echo $THIS_VAL
and the output was
[workspace] $ /bin/bash /tmp/hudson8126983335734936805.sh
HAS SPACES
HAS SPACES
HAS SPACES
Finished: SUCCESS
I think what you need to do is use the following:
--platform="${VARIABLE_NAME}"
NOTE: Use double quotes, not single quotes. Using single quotes makes the stuff inside the quotes literal, meaning that any variables will be printed as is, not parsed into the actual value. Therefore '${VARIABLE_NAME}' will be printed as is, not parsed into "Windows 7".
EDIT:
Based on #BobSilverberg comment below, use the following:
--platform="$VARIABLE_NAME"
Note: no curly brackets.

Resources