I'm trying to develop an extension/pipeline task for Azure DevOps for release gates. Not getting how to bind the the latest value to the pick list as shown here:
Each time a build completes, pick list should have latest build Id as the selected value.
Here is the code snippet i tried,
This creates a pick list
{
"name": "buildId",
"type": "pickList",
"label": "Artifact",
"required": true,
"defaultValue": "Latest",
"properties": {
"DisableManageLink": "True",
"EditableOptions": "True"
}
"helpMarkDown": "Build Artifacts"
},
this binds the values for the picklist
{
"target": "buildId",
"endpointId": "tfs:teamfoundation",
"endpointUrl": "{{endpoint.url}}/{{system.teamProject}}/_apis/build/builds?api-version=5.0",
"resultSelector": "jsonpath:$.value[*]",
"resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{buildNumber}}} \" }"
}
Let me know if there are any way to get this.
I afraid you will need to implement your own code methods to bind latest value to the latest buildid.
For example in Publish build artifacts task. The latest build id is retrieved according to the selected value(ie. latest) of field buildVersionToDownload: See below:
buildVersionToDownload has three options.
{
"name": "buildVersionToDownload",
"type": "pickList",
"label": "Build version to download",
"defaultValue": "latest",
"visibleRule": "buildType == specific",
"required": true,
"options": {
"latest": "Latest",
"latestFromBranch": "Latest from specific branch and specified Build Tags",
"specific": "Specific version"
}
}
If the buildVersionToDownload value is selected as specific. Then it is required to select the build id in the Build picklist.
{
"name": "buildId",
"type": "pickList",
"label": "Build",
"defaultValue": "",
"required": true,
"visibleRule": "buildType == specific && buildVersionToDownload == specific",
"properties": {
"EditableOptions": "True",
"DisableManageLink": "True"
},
"helpMarkDown": "The build from which to download the artifacts"
}
buildVersionToDownload is not bound to dataSource, only buildId is bound:
{
"endpointId": "tfs:teamfoundation",
"target": "buildId",
"endpointUrl": "{{endpoint.url}}/{{project}}/_apis/build/builds?definitions={{definition}}&resultFilter=succeeded,partiallySucceeded&$top=200",
"resultSelector": "jsonpath:$.value[*]",
"parameters": {
"project": "$(project)",
"definition": "$(definition)"
},
"resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{buildNumber}}}\" }"
}
See here for more information.
If buildVersionToDownload is selected as latest. Then the latest build id is retrieved via code. See code example here
So that in your custom task. When the Default Version is latest. You can get the latest build id in your code methods. And when Default Version is not latest. Then make the Artifact visible to select a build id from the picklist.
I have the following definition in my pipeline that I am running on Azure DevOps Server Version Dev17.M153.3
After saving the change I can see the following content has been added to pipeline definition
"approvals": [
{
"rank": 1,
"isAutomated": false,
"isNotificationOn": false,
"approver": {
"displayName": "Aouslender, Alexey",
"url": "http://tdc1tfsapp01:8080/tfs/DefaultCollection/_apis/Identities/2d86d86b-fe02-4e22-aa53-4315cdb3821c",
"_links": {
"avatar": {
"href": "http://tdc1tfsapp01:8080/tfs/DefaultCollection/_apis/GraphProfile/MemberAvatars/win.Uy0xLTUtMjEtMzMwNDk4NzQ2Ni0xODkxMDA3NDIzLTI5MjUxNTc3OTctNDU4NDA1"
}
},
"id": "2d86d86b-fe02-4e22-aa53-4315cdb3821c",
"uniqueName": "DOMAIN\\PXXXXXX",
"imageUrl": "http://tdc1tfsapp01:8080/tfs/DefaultCollection/_apis/GraphProfile/MemberAvatars/win.Uy0xLTUtMjEtMzMwNDk4NzQ2Ni0xODkxMDA3NDIzLTI5MjUxNTc3OTctNDU4NDA1",
"descriptor": "win.Uy0xLTUtMjEtMzMwNDk4NzQ2Ni0xODkxMDA3NDIzLTI5MjUxNTc3OTctNDU4NDA1"
},
"id": 3546
}
]
Now I am exporting pipeline, using export option. Then I am deleting pipeline and importing it using exported json file.
The imported pipeline missing the Approvers definition, nevertheless I can see the definition in exported json.
"preDeployApprovals": {
"approvals": [
{
"rank": 1,
"isAutomated": false,
"isNotificationOn": false,
"approver": {
"displayName": "Aouslender, Alexey",
"url": "http://tdc1tfsapp01:8080/tfs/DefaultCollection/_apis/Identities/2d86d86b-fe02-4e22-aa53-4315cdb3821c",
"_links": {
"avatar": {
"href": "http://tdc1tfsapp01:8080/tfs/DefaultCollection/_apis/GraphProfile/MemberAvatars/win.Uy0xLTUtMjEtMzMwNDk4NzQ2Ni0xODkxMDA3NDIzLTI5MjUxNTc3OTctNDU4NDA1"
}
},
"id": "2d86d86b-fe02-4e22-aa53-4315cdb3821c",
"uniqueName": "DOMAIN\\PXXXXXX",
"imageUrl": "http://tdc1tfsapp01:8080/tfs/DefaultCollection/_apis/GraphProfile/MemberAvatars/win.Uy0xLTUtMjEtMzMwNDk4NzQ2Ni0xODkxMDA3NDIzLTI5MjUxNTc3OTctNDU4NDA1",
"descriptor": "win.Uy0xLTUtMjEtMzMwNDk4NzQ2Ni0xODkxMDA3NDIzLTI5MjUxNTc3OTctNDU4NDA1"
},
"id": 3535
}
],
"approvalOptions": {
"requiredApproverCount": null,
"releaseCreatorCanBeApprover": true,
"autoTriggeredAndPreviousEnvironmentApprovedCanBeSkipped": false,
"enforceIdentityRevalidation": false,
"timeoutInMinutes": 0,
"executionOrder": 1
}
}
Am I missing something here or is it actually a Microsoft bug?
It's by design. Following properties in the release definition are not imported: Agent Queues, Deployment Groups, Deployment Group Tags, Approvals, Variable Groups and values of secret variables.
Generally if in the same team project you can Clone the release definition directly.
I have an Angular 9 project and I am trying to get the minimal code coverage working on an pull request in Azure devops conform the documentation. However the minimal code coverage isn't working, probably missing some step....
Steps to reproduce:
Create an new Angular 9 project: "ng new DefaultWebsite"
Create build pipeline and edit the karma and protractor config conform Microsoft "Build, test, and deploy JavaScript and Node.js apps" documentation
Add and "azurepipelines-coverage.yml" in the root of my project to enable the code coverage check in an pull request conform the Microsoft "Code coverage for pull requests" documentation
Disable some test in de app.spec.ts file, so the code coverage isn't 100% any more, it now is 77%. Changed the minimal code coverag in the yml file to 95% so the pull request cannot be complited conform the theory and it should give an "Coverage status check failed" error conform the Microsoft documentation.
However when the pull request is started there is an code coverage check below the 'Status' part. When the build (with unit and e2e tests) is done, there is no code coverage error which I espect te see below the 'Status' part.
Pull reqeust with code coverage check Pull reqeust build completed
When I look at the build there are test results and code coverage results.
Build test result Build code coverage result
When I look at the code coverage result I see an Line coverage of 75% which should be an minimal of 90% conform the yml-file.
Karma config file
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
const process = require('process');
process.env.CHROME_BIN = require('puppeteer').executablePath();
config.set({
basePath: '',
frameworks: ['jasmine', '#angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('#angular-devkit/build-angular/plugins/karma'),
require('karma-junit-reporter')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage'),
reports: ['html', 'lcovonly', 'text-summary', 'cobertura'],
fixWebpackSourcePaths: true
},
coverageReporter: {
type : 'html',
dir : 'coverage/'
},
junitReporter: {
outputDir: './coverage', // results will be saved as $outputDir/$browserName.xml
outputFile: 'junit.xml', // if included, results will be saved as $outputDir/$browserName/$outputFile
suite: '', // suite will become the package name attribute in xml testsuite element
useBrowserName: true, // add browser name to report and classes names
nameFormatter: undefined, // function (browser, result) to customize the name attribute in xml testcase element
classNameFormatter: undefined, // function (browser, result) to customize the classname attribute in xml testcase element
properties: {}, // key value pair of properties to add to the <properties> section of the report
xmlVersion: null // use '1' if reporting to be per SonarQube 6.2 XML format
},
reporters: ['progress', 'kjhtml','junit'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['ChromeHeadless'],
singleRun: false,
restartOnFileChange: true
});
};
Protractor config file:
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
const { JUnitXmlReporter } = require('jasmine-reporters');
process.env.CHROME_BIN = process.env.CHROME_BIN || require("puppeteer").executablePath();
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome',
chromeOptions: {
args: ["--headless", "--disable-gpu", "--window-size=1200,900"],
binary: process.env.CHROME_BIN
}
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function () { }
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
var junitReporter = new JUnitXmlReporter({
savePath: require('path').join(__dirname, './junit'),
consolidateAll: true
});
jasmine.getEnv().addReporter(junitReporter);
}
};
azurepipelines-coverage.yml
coverage:
status: #Code coverage status will be posted to pull requests based on targets defined below.
diff: #diff coverage is code coverage only for the lines changed in a pull request.
target: 95% #set this to a desired %. Default is 70%.
Azure Build pipeline steps:
"steps": [
{
"environment": {},
"enabled": true,
"continueOnError": false,
"alwaysRun": false,
"displayName": "npm install",
"timeoutInMinutes": 0,
"condition": "succeeded()",
"task": {
"id": "fe47e961-9fa8-4106-8639-368c022d43ad",
"versionSpec": "1.*",
"definitionType": "task"
},
"inputs": {
"command": "install",
"workingDir": "Project\\Frontend\\DefaultWebsite",
"verbose": "false",
"customCommand": "",
"customRegistry": "useNpmrc",
"customFeed": "",
"customEndpoint": "",
"publishRegistry": "useExternalRegistry",
"publishFeed": "",
"publishPackageMetadata": "true",
"publishEndpoint": ""
}
},
{
"environment": {},
"enabled": true,
"continueOnError": false,
"alwaysRun": false,
"displayName": "npm custom - test ",
"timeoutInMinutes": 0,
"condition": "succeeded()",
"task": {
"id": "fe47e961-9fa8-4106-8639-368c022d43ad",
"versionSpec": "1.*",
"definitionType": "task"
},
"inputs": {
"command": "custom",
"workingDir": "Project\\Frontend\\DefaultWebsite",
"verbose": "false",
"customCommand": "run test",
"customRegistry": "useNpmrc",
"customFeed": "",
"customEndpoint": "",
"publishRegistry": "useExternalRegistry",
"publishFeed": "",
"publishPackageMetadata": "true",
"publishEndpoint": ""
}
},
{
"environment": {},
"enabled": true,
"continueOnError": false,
"alwaysRun": false,
"displayName": "Publish code coverage from Project\\Frontend\\DefaultWebsite\\coverage\\cobertura-coverage.xml",
"timeoutInMinutes": 0,
"condition": "succeeded()",
"task": {
"id": "2a7ebc54-c13e-490e-81a5-d7561ab7cd97",
"versionSpec": "1.*",
"definitionType": "task"
},
"inputs": {
"codeCoverageTool": "Cobertura",
"summaryFileLocation": "Project\\Frontend\\DefaultWebsite\\coverage\\cobertura-coverage.xml",
"pathToSources": "",
"reportDirectory": "",
"additionalCodeCoverageFiles": "",
"failIfCoverageEmpty": "false"
}
},
{
"environment": {},
"enabled": true,
"continueOnError": false,
"alwaysRun": false,
"displayName": "Publish Test Results Project\\Frontend\\DefaultWebsite\\**\\junit.xml copy",
"timeoutInMinutes": 0,
"condition": "succeeded()",
"task": {
"id": "0b0f01ed-7dde-43ff-9cbb-e48954daf9b1",
"versionSpec": "2.*",
"definitionType": "task"
},
"inputs": {
"testRunner": "JUnit",
"testResultsFiles": "Project\\Frontend\\DefaultWebsite\\**\\junit.xml",
"searchFolder": "$(System.DefaultWorkingDirectory)",
"mergeTestResults": "false",
"failTaskOnFailedTests": "false",
"testRunTitle": "",
"platform": "",
"configuration": "",
"publishRunAttachments": "true"
}
},
{
"environment": {},
"enabled": true,
"continueOnError": false,
"alwaysRun": false,
"displayName": "npm custom - e2e",
"timeoutInMinutes": 0,
"condition": "succeeded()",
"task": {
"id": "fe47e961-9fa8-4106-8639-368c022d43ad",
"versionSpec": "1.*",
"definitionType": "task"
},
"inputs": {
"command": "custom",
"workingDir": "Project\\Frontend\\DefaultWebsite",
"verbose": "false",
"customCommand": "run e2e",
"customRegistry": "useNpmrc",
"customFeed": "",
"customEndpoint": "",
"publishRegistry": "useExternalRegistry",
"publishFeed": "",
"publishPackageMetadata": "true",
"publishEndpoint": ""
}
},
{
"environment": {},
"enabled": true,
"continueOnError": false,
"alwaysRun": false,
"displayName": "Publish Test Results Project\\Frontend\\DefaultWebsite\\e2e\\**\\junitresults.xml",
"timeoutInMinutes": 0,
"condition": "succeeded()",
"task": {
"id": "0b0f01ed-7dde-43ff-9cbb-e48954daf9b1",
"versionSpec": "2.*",
"definitionType": "task"
},
"inputs": {
"testRunner": "JUnit",
"testResultsFiles": "Project\\Frontend\\DefaultWebsite\\e2e\\**\\junitresults.xml",
"searchFolder": "$(System.DefaultWorkingDirectory)",
"mergeTestResults": "false",
"failTaskOnFailedTests": "false",
"testRunTitle": "",
"platform": "",
"configuration": "",
"publishRunAttachments": "true"
}
},
{
"environment": {},
"enabled": true,
"continueOnError": false,
"alwaysRun": false,
"displayName": "npm custom - prodBuild",
"timeoutInMinutes": 0,
"condition": "succeeded()",
"task": {
"id": "fe47e961-9fa8-4106-8639-368c022d43ad",
"versionSpec": "1.*",
"definitionType": "task"
},
"inputs": {
"command": "custom",
"workingDir": "Project\\Frontend\\DefaultWebsite",
"verbose": "false",
"customCommand": "run prodBuild",
"customRegistry": "useNpmrc",
"customFeed": "",
"customEndpoint": "",
"publishRegistry": "useExternalRegistry",
"publishFeed": "",
"publishPackageMetadata": "true",
"publishEndpoint": ""
}
},
{
"environment": {},
"enabled": true,
"continueOnError": false,
"alwaysRun": false,
"displayName": "Publish Artifact: app",
"timeoutInMinutes": 0,
"condition": "succeeded()",
"task": {
"id": "2ff763a7-ce83-4e1f-bc89-0ae63477cebe",
"versionSpec": "1.*",
"definitionType": "task"
},
"inputs": {
"PathtoPublish": "Project\\Frontend\\DefaultWebsite\\dist",
"ArtifactName": "app",
"ArtifactType": "Container",
"TargetPath": "",
"Parallel": "false",
"ParallelCount": "8",
"FileCopyOptions": ""
}
}
],
I'm trying to replicate an Azure DevOps process from one organization to another via the AZDO REST Api. I'm working on replicating the layout and am stuck because I can't discover the relationship between a custom field and a picklist when querying the source AZDO instance.
In my scenario I have a test work item type which I've called Issue. On the Issue interface I've created a custom field which is a picklist. While I can retrieve a list of lists via the Rest API and examine the field as well, I can't figure out how the two are related.
Here is a partial payload from the field:
{
"count": 39,
"value": [
...
{
"referenceName": "Custom.IssueSource",
"name": "Issue Source",
"type": "string",
"description": "Who is this attributed to",
"required": true,
"url": "https://dev.azure.com/MYORG/_apis/work/processes/f390103e-7097-4f19-b5b5-f9dbcf92bb6f/behaviors",
"customization": "custom"
},
... ]
}
and here is a partial payload from the lists get query which I used trial and error to determine was the picklist I've assigned:
{
"count": 10,
"value": [
...
{
"id": "2998d4e4-2bec-4935-98a1-b67a0b0b6d5d",
"name": "picklist_e854661e-8620-4ad9-be28-b974c5cb3a5d",
"type": "String",
"isSuggested": false,
"url": "https://dev.azure.com/MYORG/_apis/work/processes/lists/2998d4e4-2bec-4935-98a1-b67a0b0b6d5d"
},
...
]
}
Here is a partial layout response for the WIT:
{
"pages": [
{
"id": "d0171d51-ff84-4038-afc1-8800ab613160.System.WorkItemType.Details",
"inherited": true,
"label": "Details",
"pageType": "custom",
"visible": true,
"isContribution": false,
"sections": [
{
"id": "Section1",
"groups": [
...
{
"id": "bf03e049-5062-4d82-b91d-4396541fbed2",
"label": "Custom",
"isContribution": false,
"visible": true,
"controls": [
{
"id": "Custom.IssueSource",
"label": "Issue Source",
"controlType": "FieldControl",
"readOnly": false,
"visible": true,
"isContribution": false
}
]
}
]
},
... ]
}
Using fiddler against the AZDO web interface, the only time I see a reference to the picklist is from another non-AZDO API to https://dev.azure.com/MYORG/_apis/Contribution/dataProviders/query
Is there a way to discover the link via the AZDO Rest API? I saw this question which was similar but was about creating the link
Figured it out. Turns out you need to query from a different scope - work item tracking rather than work item tracking process:
https://dev.azure.com/MYORG/_apis/wit/fields/Custom.IssueSource?api-version=5.0-preview.2
returns
{
"name": "Issue Source",
"referenceName": "Custom.IssueSource",
"description": "Who is this attributed to",
"type": "string",
"usage": "workItem",
"readOnly": false,
"canSortBy": true,
"isQueryable": true,
...
"isIdentity": false,
--> "isPicklist": true,
"isPicklistSuggested": false,
--> "picklistId": "2998d4e4-2bec-4935-98a1-b67a0b0b6d5d",
"url": "https://dev.azure.com/MYORG/_apis/wit/fields/Custom.IssueSource"
}
In Azure Devops, I am building one extension with build task where input picklist field is dependent on the another input filepath field.
When the user provides an input filepath, I wanted to dynamically read and populate the picklist with new items.
I tried to search and implement but couldn't find a way.
Any help or guidance will be appreciated. I found the below example to help me out for static values.
{
"name": "action",
"type": "pickList",
"label": "Action",
"defaultValue": "Publish",
"required": true,
"helpMarkDown": "Select the Action to perform",
"options": {
"Publish": "Publish Changes",
"Script": "Script Changes",
"DeployReport": "Generate Deployment Report"
}
}
The out-of-box tasks are open source, so you can take a look through the repository to get some ideas of the patterns they use. Here is an example from the Download Build Artifacts task. The build definition is dependent on the project that is defined.
{
"name": "project",
"type": "pickList",
"label": "Project",
"defaultValue": "",
"required": true,
"visibleRule": "buildType == specific",
"properties": {
"EditableOptions": "True",
"DisableManageLink": "True"
},
"helpMarkDown": "The project from which to download the build artifacts"
},
{
"name": "definition",
"aliases": [
"pipeline"
],
"type": "pickList",
"label": "Build pipeline",
"defaultValue": "",
"required": true,
"visibleRule": "buildType == specific",
"properties": {
"EditableOptions": "True",
"DisableManageLink": "True",
"IsSearchable": "True"
},
"helpMarkDown": "Select the build pipeline name"
},
Then in the dataSourceBindings section believe it uses the target from the previous selection as a parameter input.
"dataSourceBindings": [
{
"endpointId": "tfs:teamfoundation",
"target": "project",
"endpointUrl": "{{endpoint.url}}/_apis/projects?$skip={{skip}}&$top=1000",
"resultSelector": "jsonpath:$.value[?(#.state=='wellFormed')]",
"resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{name}}}\" }",
"callbackContextTemplate": "{\"skip\": \"{{add skip 1000}}\"}",
"callbackRequiredTemplate": "{{isEqualNumber result.count 1000}}",
"initialContextTemplate": "{\"skip\": \"0\"}"
},
{
"endpointId": "tfs:teamfoundation",
"target": "definition",
"endpointUrl": "{{endpoint.url}}/{{project}}/_apis/build/definitions?api-version=3.0-preview&$top=500&continuationToken={{{continuationToken}}}&name=*{{name}}*&queryOrder=2",
"resultSelector": "jsonpath:$.value[?(#.quality=='definition')]",
"parameters": {
"project": "$(project)",
"name": "$(name)"
},
"resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{name}}}\" }",
"callbackContextTemplate": "{\"continuationToken\" : \"{{{headers.x-ms-continuationtoken}}}\"}",
"callbackRequiredTemplate": "{{{#headers.x-ms-continuationtoken}}}true{{{/headers.x-ms-continuationtoken}}}",
"initialContextTemplate": "{\"continuationToken\" : \"{{{system.utcNow}}}\"}"
},