├── images ├── icon.png └── icon.svg ├── Tasks ├── SaveCacheV1 │ ├── icon.png │ ├── tsconfig.json │ ├── make.json │ ├── savecache.ts │ ├── package.json │ ├── Strings │ │ └── resources.resjson │ │ │ └── en-US │ │ │ └── resources.resjson │ ├── task.loc.json │ └── task.json ├── RestoreCacheV1 │ ├── icon.png │ ├── tsconfig.json │ ├── make.json │ ├── restorecache.ts │ ├── package.json │ ├── Strings │ │ └── resources.resjson │ │ │ └── en-US │ │ │ └── resources.resjson │ ├── task.loc.json │ └── task.json ├── RestoreAndSaveCacheV1 │ ├── icon.png │ ├── Tests │ │ ├── Constants.ts │ │ ├── SaveCacheFromFork.ts │ │ ├── RestoreCacheFromFork.ts │ │ ├── SaveCacheNoKeyFiles.ts │ │ ├── RestoreCacheNoKeyFiles.ts │ │ ├── SaveCacheNoTargetFolders.ts │ │ ├── RestoreCacheDryRunCacheNotExists.ts │ │ ├── RestoreCacheDryRunCacheExists.ts │ │ ├── SaveCacheNoHashMatch.ts │ │ ├── SaveCacheCacheHit.ts │ │ ├── RestoreCacheArtifactToolErr.ts │ │ ├── RestoreCacheCacheHit.ts │ │ ├── RestoreCacheCacheMiss.ts │ │ ├── RestoreCacheCacheHitCacheAlias.ts │ │ ├── RestoreCacheCacheHitPlatIndependent.ts │ │ ├── RestoreCacheCacheMissDotKeyFile.ts │ │ ├── RestoreCacheCacheMissCacheAlias.ts │ │ ├── RestoreCachePermissionsError.ts │ │ ├── SaveCacheCacheMissDotKeyFile.ts │ │ ├── SaveCacheCacheMissDotTarget.ts │ │ ├── SaveCacheArtifactToolErr.ts │ │ ├── SaveCacheCacheMiss.ts │ │ └── SaveCachePermissionsError.ts │ ├── tsconfig.json │ ├── make.json │ ├── savecache.ts │ ├── restorecache.ts │ ├── package.json │ ├── Strings │ │ └── resources.resjson │ │ │ └── en-US │ │ │ └── resources.resjson │ ├── task.loc.json │ └── task.json └── Common │ └── packaging-common │ ├── tsconfig.json │ ├── package.json │ ├── Tests │ ├── FeedUtilityMockHelper.ts │ ├── ArtifactToolMockHelper.ts │ ├── MockHelper.ts │ └── UniversalMockHelper.ts │ ├── cache │ ├── feedUtilities.ts │ ├── Authentication.ts │ ├── universalPackages.ts │ ├── universaldownload.ts │ └── universalpublish.ts │ ├── ArtifactToolRunner.ts │ ├── module.json │ ├── Strings │ └── resources.resjson │ │ ├── de-de │ │ └── resources.resjson │ │ ├── en-US │ │ └── resources.resjson │ │ ├── es-es │ │ └── resources.resjson │ │ ├── fr-fr │ │ └── resources.resjson │ │ ├── it-IT │ │ └── resources.resjson │ │ ├── ja-jp │ │ └── resources.resjson │ │ ├── ko-KR │ │ └── resources.resjson │ │ ├── ru-RU │ │ └── resources.resjson │ │ ├── zh-CN │ │ └── resources.resjson │ │ └── zh-TW │ │ └── resources.resjson │ ├── ArtifactToolUtilities.ts │ ├── provenance.ts │ └── locationUtilities.ts ├── tsconfig.json ├── .eslintrc.js ├── make-options.json ├── gulpfile.js ├── LICENSE ├── .vscode └── launch.json ├── package.json ├── ci └── continuous-build.yml ├── vss-extension.json ├── .gitignore └── README.md /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/azure-pipelines-artifact-caching-tasks/HEAD/images/icon.png -------------------------------------------------------------------------------- /Tasks/SaveCacheV1/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/azure-pipelines-artifact-caching-tasks/HEAD/Tasks/SaveCacheV1/icon.png -------------------------------------------------------------------------------- /Tasks/RestoreCacheV1/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/azure-pipelines-artifact-caching-tasks/HEAD/Tasks/RestoreCacheV1/icon.png -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/azure-pipelines-artifact-caching-tasks/HEAD/Tasks/RestoreAndSaveCacheV1/icon.png -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/Constants.ts: -------------------------------------------------------------------------------- 1 | export module Constants { 2 | export const Hash = 3 | "2f6b1287b26ff4716cffdeeabd434aa1a3da9f092ebf87579a916ca0bf91cd65"; 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "sourceMap": true, 7 | "types": ["mocha", "node"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Tasks/SaveCacheV1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs", 5 | "sourceMap": true 6 | }, 7 | "exclude": [ 8 | "node_modules" 9 | ], 10 | "types": ["mocha", "node"] 11 | } -------------------------------------------------------------------------------- /Tasks/RestoreCacheV1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs", 5 | "sourceMap": true 6 | }, 7 | "exclude": [ 8 | "node_modules" 9 | ], 10 | "types": ["mocha", "node"] 11 | } -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs", 5 | "sourceMap": true 6 | }, 7 | "exclude": [ 8 | "node_modules" 9 | ], 10 | "types": ["mocha", "node"] 11 | } -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "declaration": true, 6 | "noImplicitAny": false, 7 | "sourceMap": true 8 | }, 9 | "exclude": [ 10 | "node_modules" 11 | ] 12 | } -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: "@typescript-eslint/parser", 3 | plugins: ["@typescript-eslint", "prettier"], 4 | extends: ["plugin:prettier/recommended"], 5 | rules: { 6 | "prettier/prettier": "error", 7 | "eslint.validate": [ 8 | "javascript", 9 | "javascriptreact", 10 | "typescript", 11 | "typescriptreact" 12 | ], 13 | semi: "error" 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /Tasks/SaveCacheV1/make.json: -------------------------------------------------------------------------------- 1 | { 2 | "common": [ 3 | { 4 | "module": "../Common/packaging-common", 5 | "type": "node", 6 | "compile": true 7 | } 8 | ], 9 | "rm": [ 10 | { 11 | "items": [ 12 | "node_modules/packaging-common/node_modules/vsts-task-lib" 13 | ], 14 | "options": "-Rf" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /Tasks/RestoreCacheV1/make.json: -------------------------------------------------------------------------------- 1 | { 2 | "common": [ 3 | { 4 | "module": "../Common/packaging-common", 5 | "type": "node", 6 | "compile": true 7 | } 8 | ], 9 | "rm": [ 10 | { 11 | "items": [ 12 | "node_modules/packaging-common/node_modules/vsts-task-lib" 13 | ], 14 | "options": "-Rf" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/make.json: -------------------------------------------------------------------------------- 1 | { 2 | "common": [ 3 | { 4 | "module": "../Common/packaging-common", 5 | "type": "node", 6 | "compile": true 7 | } 8 | ], 9 | "rm": [ 10 | { 11 | "items": [ 12 | "node_modules/packaging-common/node_modules/vsts-task-lib" 13 | ], 14 | "options": "-Rf" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /Tasks/SaveCacheV1/savecache.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as tl from 'azure-pipelines-task-lib'; 5 | import * as path from 'path'; 6 | import { cacheUtilities } from 'packaging-common/cache/cacheUtilities'; 7 | const cache = new cacheUtilities(); 8 | 9 | async function run() { 10 | tl.setResourcePath(path.join(__dirname, "task.json")); 11 | await cache.saveCache(); 12 | } 13 | 14 | run(); -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/savecache.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as tl from 'azure-pipelines-task-lib'; 5 | import * as path from 'path'; 6 | import { cacheUtilities } from 'packaging-common/cache/cacheUtilities'; 7 | const cache = new cacheUtilities(); 8 | 9 | async function run() { 10 | tl.setResourcePath(path.join(__dirname, "task.json")); 11 | await cache.saveCache(); 12 | } 13 | 14 | run(); -------------------------------------------------------------------------------- /Tasks/RestoreCacheV1/restorecache.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as tl from 'azure-pipelines-task-lib'; 5 | import * as path from 'path'; 6 | import { cacheUtilities } from 'packaging-common/cache/cacheUtilities'; 7 | const cache = new cacheUtilities(); 8 | 9 | async function run() { 10 | tl.setResourcePath(path.join(__dirname, "task.json")); 11 | await cache.restoreCache(); 12 | } 13 | 14 | run(); -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/restorecache.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as tl from 'azure-pipelines-task-lib'; 5 | import * as path from 'path'; 6 | import { cacheUtilities } from 'packaging-common/cache/cacheUtilities'; 7 | const cache = new cacheUtilities(); 8 | 9 | async function run() { 10 | tl.setResourcePath(path.join(__dirname, "task.json")); 11 | await cache.restoreCache(); 12 | } 13 | 14 | run(); -------------------------------------------------------------------------------- /make-options.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": [ 3 | "RestoreAndSaveCacheV1", 4 | "RestoreCacheV1", 5 | "SaveCacheV1" 6 | ], 7 | "taskResources": [ 8 | "*.ps1", 9 | "*.psd1", 10 | "*.psm1", 11 | "*.sh", 12 | "*.txt", 13 | "icon.png", 14 | "icon.svg", 15 | "module.json", 16 | "node_modules", 17 | "package.json", 18 | "Strings", 19 | "task.json", 20 | "task.loc.json", 21 | "Tests" 22 | ] 23 | } -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/SaveCacheFromFork.ts: -------------------------------------------------------------------------------- 1 | import * as ma from "azure-pipelines-task-lib/mock-answer"; 2 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 3 | import * as path from "path"; 4 | 5 | let taskPath = path.join(__dirname, "..", "savecache.js"); 6 | let tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 7 | 8 | tmr.setInput("keyFile", "**/*/yarn.lock"); 9 | tmr.setInput("targetFolders", "**/*/node_modules"); 10 | 11 | process.env["SYSTEM_PULLREQUEST_ISFORK"] = "true"; 12 | 13 | tmr.run(); 14 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/RestoreCacheFromFork.ts: -------------------------------------------------------------------------------- 1 | import * as ma from "azure-pipelines-task-lib/mock-answer"; 2 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 3 | import * as path from "path"; 4 | 5 | let taskPath = path.join(__dirname, "..", "restorecache.js"); 6 | let tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 7 | 8 | tmr.setInput("keyFile", "**/*/yarn.lock"); 9 | tmr.setInput("targetFolders", "**/*/node_modules"); 10 | 11 | process.env["SYSTEM_PULLREQUEST_ISFORK"] = "true"; 12 | 13 | tmr.run(); 14 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/SaveCacheNoKeyFiles.ts: -------------------------------------------------------------------------------- 1 | import * as ma from "azure-pipelines-task-lib/mock-answer"; 2 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 3 | import * as path from "path"; 4 | 5 | const taskPath = path.join(__dirname, "..", "savecache.js"); 6 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 7 | 8 | tmr.setInput("keyFile", "**/*/yarn.lock"); 9 | tmr.setInput("targetFolder", "**/*/node_modules"); 10 | 11 | // provide answers for task mock 12 | const a: ma.TaskLibAnswers = { 13 | findMatch: { 14 | "**/*/yarn.lock": [], 15 | }, 16 | rmRF: { 17 | "*": { success: true }, 18 | }, 19 | } as ma.TaskLibAnswers; 20 | 21 | tmr.setAnswers(a); 22 | 23 | tmr.run(); 24 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/RestoreCacheNoKeyFiles.ts: -------------------------------------------------------------------------------- 1 | import * as ma from "azure-pipelines-task-lib/mock-answer"; 2 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 3 | import * as path from "path"; 4 | 5 | const taskPath = path.join(__dirname, "..", "restorecache.js"); 6 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 7 | 8 | tmr.setInput("keyFile", "**/*/yarn.lock"); 9 | tmr.setInput("targetFolders", "**/*/node_modules"); 10 | 11 | // provide answers for task mock 12 | const a: ma.TaskLibAnswers = { 13 | findMatch: { 14 | "**/*/yarn.lock": [], 15 | }, 16 | rmRF: { 17 | "*": { success: true }, 18 | }, 19 | } as ma.TaskLibAnswers; 20 | 21 | tmr.setAnswers(a); 22 | 23 | tmr.run(); 24 | -------------------------------------------------------------------------------- /Tasks/SaveCacheV1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "save-cache-task", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "built/savecache.js", 6 | "author": "Microsoft Corporation", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/Microsoft/azure-pipelines-artifact-caching-tasks" 10 | }, 11 | "license": "MIT", 12 | "dependencies": { 13 | "@types/mocha": "^5.2.5", 14 | "@types/node": "^10.12.18", 15 | "@types/q": "^1.5.1", 16 | "adm-zip": "^0.4.11", 17 | "azure-devops-node-api": "^7.0.0", 18 | "azure-pipelines-task-lib": "^2.8.0", 19 | "azure-pipelines-tool-lib": "^0.12.0", 20 | "packaging-common": "file:../../_build/Tasks/Common/packaging-common-1.0.1.tgz", 21 | "shelljs": "^0.8.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tasks/RestoreCacheV1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "restore-cache-task", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "built/restorecache.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Microsoft Corporation", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Microsoft/azure-pipelines-artifact-caching-tasks" 13 | }, 14 | "license": "MIT", 15 | "dependencies": { 16 | "@types/mocha": "^5.2.5", 17 | "@types/node": "^10.12.18", 18 | "@types/q": "^1.5.1", 19 | "adm-zip": "^0.4.11", 20 | "azure-devops-node-api": "^7.0.0", 21 | "azure-pipelines-task-lib": "^2.8.0", 22 | "azure-pipelines-tool-lib": "^0.12.0", 23 | "packaging-common": "file:../../_build/Tasks/Common/packaging-common-1.0.1.tgz", 24 | "shelljs": "^0.8.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "restore-save-cache-task", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "built/restorecache.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Microsoft Corporation", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Microsoft/azure-pipelines-artifact-caching-tasks" 13 | }, 14 | "license": "MIT", 15 | "dependencies": { 16 | "@types/mocha": "^5.2.6", 17 | "@types/node": "^10.12.18", 18 | "@types/q": "^1.5.1", 19 | "adm-zip": "^0.4.11", 20 | "azure-devops-node-api": "^7.0.0", 21 | "azure-pipelines-task-lib": "^2.8.0", 22 | "azure-pipelines-tool-lib": "^0.12.0", 23 | "mocha": "^6.0.2", 24 | "packaging-common": "file:../../_build/Tasks/Common/packaging-common-1.0.1.tgz", 25 | "shelljs": "^0.8.3", 26 | "ts-node": "^8.0.3" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "packaging-common", 3 | "version": "1.0.1", 4 | "description": "Azure Pipelines Packaging Tasks Common", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "Microsoft Corporation", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/Microsoft/azure-pipelines-artifact-caching-tasks" 12 | }, 13 | "license": "MIT", 14 | "dependencies": { 15 | "@types/ltx": "^2.8.0", 16 | "@types/node": "^11.13.0", 17 | "adm-zip": "^0.4.11", 18 | "axios": "^0.19.0", 19 | "axios-mock-adapter": "^1.17.0", 20 | "azure-devops-node-api": "^6.6.0", 21 | "azure-pipelines-task-lib": "^2.8.0", 22 | "azure-pipelines-tool-lib": "^0.12.0", 23 | "ini": "^1.3.4", 24 | "ip-address": "^5.8.9", 25 | "ltx": "^2.6.2", 26 | "q": "^1.5.0", 27 | "shelljs": "^0.8.3", 28 | "ts-node": "^8.0.3", 29 | "typed-rest-client": "0.12.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/SaveCacheNoTargetFolders.ts: -------------------------------------------------------------------------------- 1 | import * as ma from "azure-pipelines-task-lib/mock-answer"; 2 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 3 | import * as path from "path"; 4 | 5 | const taskPath = path.join(__dirname, "..", "savecache.js"); 6 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 7 | 8 | tmr.setInput("keyFile", "**/*/yarn.lock"); 9 | tmr.setInput("targetFolder", "**/*/node_modules"); 10 | process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = "DefaultWorkingDirectory"; 11 | 12 | // provide answers for task mock 13 | const a: ma.TaskLibAnswers = { 14 | findMatch: { 15 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 16 | "**/*/node_modules": [], 17 | }, 18 | find: { 19 | DefaultWorkingDirectory: [ 20 | "src/webapi/startup.config", 21 | "src/application/program.cs", 22 | ], 23 | }, 24 | rmRF: { 25 | "*": { success: true }, 26 | }, 27 | } as ma.TaskLibAnswers; 28 | 29 | tmr.setAnswers(a); 30 | 31 | tmr.run(); 32 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require("gulp"); 2 | var gutil = require("gulp-util"); 3 | var child_process = require("child_process"); 4 | var process = require("process"); 5 | 6 | function make(target, cb) { 7 | var cl = ( 8 | "node make.js " + 9 | target + 10 | " " + 11 | process.argv.slice(3).join(" ") 12 | ).trim(); 13 | console.log("------------------------------------------------------------"); 14 | console.log("> " + cl); 15 | console.log("------------------------------------------------------------"); 16 | try { 17 | child_process.execSync(cl, { cwd: __dirname, stdio: "inherit" }); 18 | } catch (err) { 19 | var msg = err.output ? err.output.toString() : err.message; 20 | console.error(msg); 21 | cb(new gutil.PluginError(msg)); 22 | return false; 23 | } 24 | 25 | return true; 26 | } 27 | 28 | gulp.task("build", function(cb) { 29 | make("build", cb); 30 | }); 31 | 32 | gulp.task("test", function(cb) { 33 | make("test", cb); 34 | }); 35 | 36 | gulp.task("clean", function(cb) { 37 | make("clean", cb); 38 | }); 39 | 40 | gulp.task("default", ["clean", "build", "test"]); 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Mocha All", 11 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 12 | "args": [ 13 | "--require", "ts-node/register", 14 | "--timeout", "999999", 15 | "--colors", "--recursive", 16 | "${workspaceFolder}/Tasks/**/Tests/_suite.ts" 17 | ], 18 | "internalConsoleOptions": "openOnSessionStart" 19 | }, 20 | { 21 | "type": "node", 22 | "request": "launch", 23 | "name": "Mocha Current", 24 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 25 | "args": [ 26 | "--require", "ts-node/register", 27 | "--timeout", "999999", 28 | "--colors", "--recursive", 29 | "${relativeFile}" 30 | ], 31 | "internalConsoleOptions": "openOnSessionStart" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Tests/FeedUtilityMockHelper.ts: -------------------------------------------------------------------------------- 1 | import tmrm = require("azure-pipelines-task-lib/mock-run"); 2 | import Axios from "axios"; 3 | import MockAdapter from "axios-mock-adapter"; 4 | 5 | export interface IMockResponse { 6 | responseCode: number; 7 | data?: any; 8 | } 9 | 10 | export function registerFeedUtilityMock( 11 | tmr: tmrm.TaskMockRunner, 12 | response: IMockResponse 13 | ) { 14 | tmr.setInput("feedlist", "node-package-feed"); 15 | tmr.setInput("verbosity", "verbose"); 16 | 17 | process.env.BUILD_DEFINITIONNAME = "build definition"; 18 | process.env.AGENT_HOMEDIRECTORY = "/users/home/directory"; 19 | process.env.BUILD_SOURCESDIRECTORY = "/users/home/sources"; 20 | process.env.SYSTEM_SERVERTYPE = "hosted"; 21 | process.env.ENDPOINT_AUTH_SYSTEMVSSCONNECTION = 22 | '{"parameters":{"AccessToken":"token"},"scheme":"OAuth"}'; 23 | process.env.ENDPOINT_URL_SYSTEMVSSCONNECTION = 24 | "https://example.visualstudio.com/defaultcollection"; 25 | process.env.SYSTEM_DEFAULTWORKINGDIRECTORY = "/users/home/directory"; 26 | process.env.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI = 27 | "https://example.visualstudio.com/defaultcollection"; 28 | 29 | const mock = new MockAdapter(Axios); 30 | console.log("mocking this out"); 31 | 32 | mock.onAny().reply(response.responseCode, response.data); 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "artifact-caching", 3 | "version": "1.0.0", 4 | "description": "Tasks for caching Azure Pipelines artifacts", 5 | "main": "gulpfile.js", 6 | "scripts": { 7 | "build": "node make.js build", 8 | "test": "mocha --require ts-node/register Tasks/**/_suite.ts" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Microsoft/azure-pipelines-artifact-caching-tasks" 13 | }, 14 | "keywords": [ 15 | "Azure", 16 | "Pipelines", 17 | "build", 18 | "tasks" 19 | ], 20 | "author": "Microsoft Corporation", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/Microsoft/azure-pipelines-artifact-caching-tasks/issues" 24 | }, 25 | "homepage": "https://github.com/Microsoft/azure-pipelines-artifact-caching-tasks", 26 | "devDependencies": { 27 | "@types/mocha": "^5.2.7", 28 | "@types/node": "^11.13.0", 29 | "gulp": "^3.9.1", 30 | "gulp-util": "3.0.4", 31 | "markdown-toc": "^1.2.0", 32 | "minimatch": "3.0.2", 33 | "minimist": "1.1.1", 34 | "mocha": "^6.0.2", 35 | "mocha-junit-reporter": "^1.18.0", 36 | "node-uuid": "1.4.6", 37 | "q": "1.4.1", 38 | "semver": "4.3.3", 39 | "shelljs": "^0.3.0", 40 | "sync-request": "3.0.1", 41 | "ts-node": "^8.0.3", 42 | "typed-rest-client": "1.0.9", 43 | "typescript": "^3.4.1", 44 | "typescript-tslint-plugin": "^0.3.1", 45 | "validator": "3.33.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Tests/ArtifactToolMockHelper.ts: -------------------------------------------------------------------------------- 1 | import tmrm = require("azure-pipelines-task-lib/mock-run"); 2 | import { TaskLibAnswerExecResult} from "azure-pipelines-task-lib/mock-answer"; 3 | 4 | export function registerArtifactToolUtilitiesMock( 5 | tmr: tmrm.TaskMockRunner, 6 | toolPath: string 7 | ) { 8 | const artifactToolMocks = { 9 | getArtifactToolFromService(serviceUri, accessToken, toolName) { 10 | return toolPath; 11 | }, 12 | getPackageNameFromId( 13 | serviceUri: string, 14 | accessToken: string, 15 | feedId: string, 16 | packageId: string 17 | ) { 18 | return packageId; 19 | }, 20 | }; 21 | tmr.registerMock("packaging-common/ArtifactToolUtilities", artifactToolMocks); 22 | tmr.registerMock("../ArtifactToolUtilities", artifactToolMocks); 23 | } 24 | 25 | export function registerArtifactToolRunnerMock(tmr: tmrm.TaskMockRunner) { 26 | const mtt = require("azure-pipelines-task-lib/mock-toolrunner"); 27 | const artifactToolMocks = { 28 | getOptions() { 29 | return { 30 | cwd: process.cwd(), 31 | env: Object.assign({}, process.env), 32 | silent: false, 33 | failOnStdErr: false, 34 | ignoreReturnCode: false, 35 | windowsVerbatimArguments: false, 36 | }; 37 | }, 38 | runArtifactTool( 39 | artifactToolPath: string, 40 | command: string[], 41 | execOptions 42 | ) { 43 | const tr = new mtt.ToolRunner(artifactToolPath); 44 | tr.arg(command); 45 | return tr.execSync(execOptions); 46 | }, 47 | }; 48 | 49 | tmr.registerMock("packaging-common/ArtifactToolRunner", artifactToolMocks); 50 | tmr.registerMock("../ArtifactToolRunner", artifactToolMocks); 51 | } 52 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Tests/MockHelper.ts: -------------------------------------------------------------------------------- 1 | import tmrm = require("azure-pipelines-task-lib/mock-run"); 2 | 3 | export function registerLocationHelpersMock(tmr: tmrm.TaskMockRunner) { 4 | const mockLocationUtils = { 5 | getFeedUriFromBaseServiceUri( 6 | serviceUri: string, 7 | accesstoken: string 8 | ) { 9 | return serviceUri + "/feed"; 10 | }, 11 | getBlobstoreUriFromBaseServiceUri( 12 | serviceUri: string, 13 | accesstoken: string 14 | ) { 15 | return serviceUri + "/blobstore"; 16 | }, 17 | getPackagingUris(input) { 18 | const collectionUrl: string = "https://vsts/packagesource"; 19 | return { 20 | PackagingUris: [collectionUrl], 21 | DefaultPackagingUri: collectionUrl, 22 | }; 23 | }, 24 | getWebApiWithProxy(serviceUri: string, accessToken?: string) { 25 | return { 26 | vsoClient: { 27 | async getVersioningData( 28 | ApiVersion: string, 29 | PackagingAreaName: string, 30 | PackageAreaId: string, 31 | Obj 32 | ) { 33 | return { requestUrl: "foobar" }; 34 | }, 35 | }, 36 | }; 37 | }, 38 | 39 | getSystemAccessToken() { 40 | return "token"; 41 | }, 42 | 43 | getFeedRegistryUrl( 44 | packagingUrl: string, 45 | registryType, 46 | feedId: string, 47 | accessToken?: string 48 | ) { 49 | return packagingUrl + "/" + feedId; 50 | }, 51 | ProtocolType: { NuGet: 1, Npm: 2, Maven: 3, PyPi: 4 }, 52 | RegistryType: { 53 | npm: 1, 54 | NuGetV2: 2, 55 | NuGetV3: 3, 56 | PyPiSimple: 4, 57 | PyPiUpload: 5, 58 | }, 59 | }; 60 | tmr.registerMock('packaging-common/locationUtilities', mockLocationUtils); 61 | tmr.registerMock("../locationUtilities", mockLocationUtils); 62 | } 63 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/cache/feedUtilities.ts: -------------------------------------------------------------------------------- 1 | import * as pkgLocationUtils from "../locationUtilities"; 2 | import * as tl from "azure-pipelines-task-lib"; 3 | import Axios, { AxiosRequestConfig } from "axios"; 4 | 5 | interface IPackage { 6 | id: string; 7 | name: string; 8 | version: string; 9 | } 10 | 11 | export async function doesPackageExist(hash: string): Promise { 12 | const feedId = tl.getInput("feedList"); 13 | // Getting package name from hash 14 | const packageId = tl 15 | .getVariable("Build.DefinitionName") 16 | .replace(/\s/g, "") 17 | .substring(0, 255) 18 | .toLowerCase(); 19 | 20 | const version = `1.0.0-${hash}`; 21 | const accessToken = pkgLocationUtils.getSystemAccessToken(); 22 | const collectionUri = process.env.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI; 23 | 24 | let instance: string = ""; 25 | const legacyRegex = /https:\/\/(\S+).visualstudio.com\S+/g; 26 | const newRegex = /https:\/\/dev.azure.com\/(\S+)\//g; 27 | 28 | const legacyUrl = legacyRegex.exec(collectionUri); 29 | const newUrl = newRegex.exec(collectionUri); 30 | 31 | if (legacyUrl) { 32 | instance = legacyUrl[1]; 33 | } else if (newUrl) { 34 | instance = newUrl[1]; 35 | } else { 36 | throw `Unable to parse collection uri: '${collectionUri}'`; 37 | } 38 | 39 | const url = `https://pkgs.dev.azure.com/${instance}/_apis/packaging/feeds/${feedId}/upack/packages/${packageId}/versions/${version}?api-version=5.1-preview.1`; 40 | 41 | const config: AxiosRequestConfig = { 42 | headers: { 43 | Authorization: `Bearer ${accessToken}`, 44 | Accept: "application/json" 45 | } 46 | }; 47 | try { 48 | const result = await Axios.get(url, config); 49 | 50 | tl.debug(JSON.stringify(result.data)); 51 | 52 | return result.data.version === version; 53 | } catch (err) { 54 | tl.debug(err.toString()); 55 | 56 | return false; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/RestoreCacheDryRunCacheNotExists.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import * as path from "path"; 3 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 4 | import { 5 | registerFeedUtilityMock, 6 | IMockResponse 7 | } from "packaging-common/Tests/FeedUtilityMockHelper"; 8 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 9 | const taskPath = path.join(__dirname, "..", "restorecache.js"); 10 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 11 | 12 | const a: TaskLibAnswers = { 13 | findMatch: { 14 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 15 | "**/*/node_modules": [] 16 | }, 17 | rmRF: { 18 | "/users/home/directory/tmp_cache": { success: true } 19 | }, 20 | checkPath: {}, 21 | exec: {}, 22 | exist: {}, 23 | which: {} 24 | }; 25 | 26 | tmr.setAnswers(a); 27 | 28 | tmr.setInput("keyFile", "**/*/yarn.lock"); 29 | tmr.setInput("targetFolders", "**/*/node_modules"); 30 | tmr.setInput("dryRun", "true"); 31 | 32 | const response: IMockResponse = { 33 | responseCode: 404 34 | }; 35 | 36 | registerFeedUtilityMock(tmr, response); 37 | 38 | // mock a specific module function called in task 39 | tmr.registerMock("fs", { 40 | readFileSync( 41 | path: string, 42 | options: 43 | | string 44 | | { 45 | encoding: string; 46 | flag?: string; 47 | } 48 | ): string { 49 | if (path.endsWith("/yarn.lock")) { 50 | const segments = path.split("/"); 51 | return segments.splice(segments.length - 3).join("/"); 52 | } 53 | return fs.readFileSync(path, options); 54 | }, 55 | chmodSync: fs.chmodSync, 56 | writeFileSync: fs.writeFileSync, 57 | readdirSync: fs.readdirSync, 58 | mkdirSync: fs.mkdirSync, 59 | copyFileSync: fs.copyFileSync, 60 | statSync: fs.statSync, 61 | linkSync: fs.linkSync, 62 | symlinkSync: fs.symlinkSync 63 | }); 64 | 65 | tmr.run(); 66 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/cache/Authentication.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as tl from "azure-pipelines-task-lib"; 5 | 6 | export interface IPackageSource { 7 | accountUrl: string; 8 | isInternal: boolean; 9 | } 10 | 11 | export class InternalAuthInfo 12 | { 13 | constructor( 14 | public uriPrefixes: string[], 15 | public accessToken: string 16 | ) { 17 | } 18 | } 19 | 20 | export class ExternalAuthInfo 21 | { 22 | constructor( 23 | public packageSource: IPackageSource, 24 | public authType: ExternalAuthType) { 25 | } 26 | } 27 | 28 | export class TokenExternalAuthInfo extends ExternalAuthInfo 29 | { 30 | constructor( 31 | public packageSource: IPackageSource, 32 | public token: string) 33 | { 34 | super(packageSource, ExternalAuthType.Token); 35 | } 36 | } 37 | 38 | export enum ExternalAuthType 39 | { 40 | Token, 41 | UsernamePassword, 42 | } 43 | 44 | export function GetExternalAuthInfo(inputKey: string): ExternalAuthInfo 45 | { 46 | let externalAuthInfo: ExternalAuthInfo; 47 | let endpointName = tl.getInput(inputKey); 48 | 49 | const accountUri = tl.getEndpointUrl(endpointName, false); 50 | let externalAuth = tl.getEndpointAuthorization(endpointName, false); 51 | 52 | switch(externalAuth.scheme.toLocaleLowerCase()) { 53 | case "token": 54 | let token = externalAuth.parameters["apitoken"]; 55 | externalAuthInfo = new TokenExternalAuthInfo( 56 | { 57 | accountUrl: accountUri 58 | }, 59 | token); 60 | break; 61 | case "usernamepassword": 62 | tl.error(tl.loc("Error_AuthNotSupported")); 63 | break; 64 | default: 65 | break; 66 | } 67 | 68 | return externalAuthInfo; 69 | } 70 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/RestoreCacheDryRunCacheExists.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import * as path from "path"; 3 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 4 | import { Constants } from "./Constants"; 5 | import { 6 | registerFeedUtilityMock, 7 | IMockResponse 8 | } from "packaging-common/Tests/FeedUtilityMockHelper"; 9 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 10 | const taskPath = path.join(__dirname, "..", "restorecache.js"); 11 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 12 | 13 | const a: TaskLibAnswers = { 14 | findMatch: { 15 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 16 | "**/*/node_modules": [] 17 | }, 18 | rmRF: { 19 | "/users/home/directory/tmp_cache": { success: true } 20 | }, 21 | checkPath: {}, 22 | exec: {}, 23 | exist: {}, 24 | which: {} 25 | }; 26 | 27 | tmr.setAnswers(a); 28 | 29 | tmr.setInput("keyFile", "**/*/yarn.lock"); 30 | tmr.setInput("targetFolders", "**/*/node_modules"); 31 | tmr.setInput("dryRun", "true"); 32 | 33 | const response: IMockResponse = { 34 | responseCode: 200, 35 | data: { 36 | id: "1234-5678-90123-45678", 37 | name: "builddefinition", 38 | version: `1.0.0-${process.platform}-${Constants.Hash}` 39 | } 40 | }; 41 | 42 | registerFeedUtilityMock(tmr, response); 43 | 44 | // mock a specific module function called in task 45 | tmr.registerMock("fs", { 46 | readFileSync( 47 | path: string, 48 | options: 49 | | string 50 | | { 51 | encoding: string; 52 | flag?: string; 53 | } 54 | ): string { 55 | if (path.endsWith("/yarn.lock")) { 56 | const segments = path.split("/"); 57 | return segments.splice(segments.length - 3).join("/"); 58 | } 59 | return fs.readFileSync(path, options); 60 | }, 61 | chmodSync: fs.chmodSync, 62 | writeFileSync: fs.writeFileSync, 63 | readdirSync: fs.readdirSync, 64 | mkdirSync: fs.mkdirSync, 65 | copyFileSync: fs.copyFileSync, 66 | statSync: fs.statSync, 67 | linkSync: fs.linkSync, 68 | symlinkSync: fs.symlinkSync 69 | }); 70 | 71 | tmr.run(); 72 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/SaveCacheNoHashMatch.ts: -------------------------------------------------------------------------------- 1 | import * as ma from "azure-pipelines-task-lib/mock-answer"; 2 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 3 | import * as path from "path"; 4 | import * as fs from "fs"; 5 | 6 | const taskPath = path.join(__dirname, "..", "savecache.js"); 7 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 8 | 9 | tmr.setInput("keyFile", "**/*/yarn.lock"); 10 | tmr.setInput("targetFolder", "**/*/node_modules"); 11 | 12 | process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = "DefaultWorkingDirectory"; 13 | 14 | // provide answers for task mock 15 | const a: ma.TaskLibAnswers = { 16 | findMatch: { 17 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 18 | "**/*/node_modules": [ 19 | "src/webapi/node_modules", 20 | "src/application/node_modules" 21 | ] 22 | }, 23 | find: { 24 | DefaultWorkingDirectory: [ 25 | "src/webapi/node_modules", 26 | "src/application/node_modules", 27 | "src/webapi/startup.config", 28 | "src/application/program.cs" 29 | ] 30 | }, 31 | rmRF: { 32 | "*": { success: true } 33 | }, 34 | stats: { 35 | "src/webapi/node_modules": { 36 | isDirectory() { 37 | return true; 38 | } 39 | }, 40 | "src/application/node_modules": { 41 | isDirectory() { 42 | return true; 43 | } 44 | } 45 | } 46 | } as ma.TaskLibAnswers; 47 | 48 | tmr.setAnswers(a); 49 | 50 | // mock a specific module function called in task 51 | tmr.registerMock("fs", { 52 | readFileSync( 53 | path: string, 54 | options: 55 | | string 56 | | { 57 | encoding: string; 58 | flag?: string; 59 | } 60 | ): string { 61 | if (path.endsWith("/yarn.lock")) { 62 | const segments = path.split("/"); 63 | return segments.splice(segments.length - 3).join("/"); 64 | } 65 | return fs.readFileSync(path, options); 66 | }, 67 | chmodSync: fs.chmodSync, 68 | writeFileSync: fs.writeFileSync, 69 | readdirSync: fs.readdirSync, 70 | mkdirSync: fs.mkdirSync, 71 | copyFileSync: fs.copyFileSync, 72 | statSync: fs.statSync, 73 | linkSync: fs.linkSync, 74 | symlinkSync: fs.symlinkSync 75 | }); 76 | 77 | tmr.run(); 78 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/SaveCacheCacheHit.ts: -------------------------------------------------------------------------------- 1 | import * as ma from "azure-pipelines-task-lib/mock-answer"; 2 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 3 | import * as path from "path"; 4 | import * as fs from "fs"; 5 | import { Constants } from "./Constants"; 6 | 7 | const taskPath = path.join(__dirname, "..", "savecache.js"); 8 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 9 | 10 | tmr.setInput("keyFile", "**/*/yarn.lock"); 11 | tmr.setInput("targetFolder", "**/*/node_modules"); 12 | 13 | process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = "DefaultWorkingDirectory"; 14 | 15 | const key = `${process.platform}-${Constants.Hash}`; 16 | process.env[key.toUpperCase()] = "true"; 17 | 18 | // provide answers for task mock 19 | const a: ma.TaskLibAnswers = { 20 | findMatch: { 21 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 22 | "**/*/node_modules": [ 23 | "src/webapi/node_modules", 24 | "src/application/node_modules" 25 | ] 26 | }, 27 | find: { 28 | DefaultWorkingDirectory: [ 29 | "src/webapi/node_modules", 30 | "src/application/node_modules", 31 | "src/webapi/startup.config", 32 | "src/application/program.cs" 33 | ] 34 | }, 35 | rmRF: { 36 | "*": { success: true } 37 | }, 38 | stats: { 39 | "src/webapi/node_modules": { 40 | isDirectory() { 41 | return true; 42 | } 43 | }, 44 | "src/application/node_modules": { 45 | isDirectory() { 46 | return true; 47 | } 48 | } 49 | } 50 | } as ma.TaskLibAnswers; 51 | 52 | tmr.setAnswers(a); 53 | 54 | // mock a specific module function called in task 55 | tmr.registerMock("fs", { 56 | readFileSync( 57 | path: string, 58 | options: 59 | | string 60 | | { 61 | encoding: string; 62 | flag?: string; 63 | } 64 | ): string { 65 | if (path.endsWith("/yarn.lock")) { 66 | const segments = path.split("/"); 67 | return segments.splice(segments.length - 3).join("/"); 68 | } 69 | return fs.readFileSync(path, options); 70 | }, 71 | chmodSync: fs.chmodSync, 72 | writeFileSync: fs.writeFileSync, 73 | readdirSync: fs.readdirSync, 74 | mkdirSync: fs.mkdirSync, 75 | copyFileSync: fs.copyFileSync, 76 | statSync: fs.statSync, 77 | linkSync: fs.linkSync, 78 | symlinkSync: fs.symlinkSync 79 | }); 80 | 81 | tmr.run(); 82 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/RestoreCacheArtifactToolErr.ts: -------------------------------------------------------------------------------- 1 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 5 | import { UniversalMockHelper } from "packaging-common/Tests/UniversalMockHelper"; 6 | import { Constants } from "./Constants"; 7 | 8 | const taskPath = path.join(__dirname, "..", "restorecache.js"); 9 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 10 | 11 | const a: TaskLibAnswers = { 12 | findMatch: { 13 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 14 | "**/*/node_modules": [] 15 | }, 16 | rmRF: { 17 | "/users/home/directory/tmp_cache": { success: true } 18 | }, 19 | checkPath: {}, 20 | exec: {}, 21 | exist: {}, 22 | which: {} 23 | }; 24 | 25 | tmr.setAnswers(a); 26 | const umh: UniversalMockHelper = new UniversalMockHelper(tmr, a, null); 27 | 28 | umh.mockUniversalCommand( 29 | "download", 30 | "node-package-feed", 31 | "builddefinition1", 32 | `1.0.0-${process.platform}-${Constants.Hash}`, 33 | "/users/home/directory/tmp_cache", 34 | { 35 | code: 0, 36 | stdout: "ArtifactTool.exe output", 37 | stderr: "" 38 | } 39 | ); 40 | 41 | tmr.setInput("keyFile", "**/*/yarn.lock"); 42 | tmr.setInput("targetFolders", "**/*/node_modules"); 43 | 44 | // mock a specific module function called in task 45 | tmr.registerMock("fs", { 46 | readFileSync( 47 | path: string, 48 | options: 49 | | string 50 | | { 51 | encoding: string; 52 | flag?: string; 53 | } 54 | ): string { 55 | if (path.endsWith("/yarn.lock")) { 56 | const segments = path.split("/"); 57 | return segments.slice(segments.length - 3).join("/"); 58 | } 59 | return fs.readFileSync(path, options); 60 | }, 61 | chmodSync: fs.chmodSync, 62 | writeFileSync: fs.writeFileSync, 63 | readdirSync: fs.readdirSync, 64 | mkdirSync: fs.mkdirSync, 65 | copyFileSync: fs.copyFileSync, 66 | statSync: fs.statSync, 67 | linkSync: fs.linkSync, 68 | symlinkSync: fs.symlinkSync 69 | }); 70 | 71 | tmr.registerMock("shelljs", { 72 | exec(command: string) { 73 | console.log(`Mock executing command: ${command}`); 74 | return { 75 | code: 0, 76 | stdout: "shelljs output", 77 | stderr: null 78 | }; 79 | } 80 | }); 81 | 82 | tmr.run(); 83 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/RestoreCacheCacheHit.ts: -------------------------------------------------------------------------------- 1 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 5 | import { UniversalMockHelper } from "packaging-common/Tests/UniversalMockHelper"; 6 | import { Constants } from "./Constants"; 7 | 8 | const taskPath = path.join(__dirname, "..", "restorecache.js"); 9 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 10 | 11 | const a: TaskLibAnswers = { 12 | findMatch: { 13 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 14 | "**/*/node_modules": [] 15 | }, 16 | rmRF: { 17 | "/users/home/directory/tmp_cache": { success: true } 18 | }, 19 | checkPath: {}, 20 | exec: {}, 21 | exist: {}, 22 | which: {} 23 | }; 24 | 25 | tmr.setAnswers(a); 26 | 27 | const umh: UniversalMockHelper = new UniversalMockHelper( 28 | tmr, 29 | a, 30 | "/users/tmp/ArtifactTool.exe" 31 | ); 32 | 33 | umh.mockUniversalCommand( 34 | "download", 35 | "node-package-feed", 36 | "builddefinition1", 37 | `1.0.0-${process.platform}-${Constants.Hash}`, 38 | "/users/home/directory/tmp_cache", 39 | { 40 | code: 0, 41 | stdout: "ArtifactTool.exe output", 42 | stderr: "" 43 | } 44 | ); 45 | 46 | tmr.setInput("keyFile", "**/*/yarn.lock"); 47 | tmr.setInput("targetFolders", "**/*/node_modules"); 48 | 49 | // mock a specific module function called in task 50 | tmr.registerMock("fs", { 51 | readFileSync( 52 | path: string, 53 | options: 54 | | string 55 | | { 56 | encoding: string; 57 | flag?: string; 58 | } 59 | ): string { 60 | if (path.endsWith("/yarn.lock")) { 61 | const segments = path.split("/"); 62 | return segments.splice(segments.length - 3).join("/"); 63 | } 64 | return fs.readFileSync(path, options); 65 | }, 66 | chmodSync: fs.chmodSync, 67 | writeFileSync: fs.writeFileSync, 68 | readdirSync: fs.readdirSync, 69 | mkdirSync: fs.mkdirSync, 70 | copyFileSync: fs.copyFileSync, 71 | statSync: fs.statSync, 72 | linkSync: fs.linkSync, 73 | symlinkSync: fs.symlinkSync 74 | }); 75 | 76 | tmr.registerMock("shelljs", { 77 | exec(command: string) { 78 | console.log(`Mock executing command: ${command}`); 79 | return { 80 | code: 0, 81 | stdout: "shelljs output", 82 | stderr: null 83 | }; 84 | } 85 | }); 86 | 87 | tmr.run(); 88 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Tests/UniversalMockHelper.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TaskLibAnswers, 3 | TaskLibAnswerExecResult 4 | } from "azure-pipelines-task-lib/mock-answer"; 5 | import tmrm = require("azure-pipelines-task-lib/mock-run"); 6 | import * as pkgMock from "./MockHelper"; 7 | import * as artMock from "./ArtifactToolMockHelper"; 8 | 9 | export class UniversalMockHelper { 10 | constructor( 11 | private tmr: tmrm.TaskMockRunner, 12 | private answers: TaskLibAnswers, 13 | private artifactToolCmd: string 14 | ) { 15 | this.tmr.setInput("verbosity", "verbose"); 16 | this.tmr.setInput("feedlist", "node-package-feed"); 17 | 18 | process.env.AGENT_HOMEDIRECTORY = "/users/home/directory"; 19 | (process.env.BUILD_SOURCESDIRECTORY = "/users/home/sources"), 20 | (process.env.SYSTEM_SERVERTYPE = "hosted"); 21 | process.env.BUILD_DEFINITIONNAME = "build definition 1"; 22 | process.env.ENDPOINT_AUTH_SYSTEMVSSCONNECTION = 23 | '{"parameters":{"AccessToken":"token"},"scheme":"OAuth"}'; 24 | process.env.ENDPOINT_URL_SYSTEMVSSCONNECTION = 25 | "https://example.visualstudio.com/defaultcollection"; 26 | process.env.SYSTEM_DEFAULTWORKINGDIRECTORY = "/users/home/directory"; 27 | process.env.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI = 28 | "https://example.visualstudio.com/defaultcollection"; 29 | 30 | artMock.registerArtifactToolUtilitiesMock(tmr, this.artifactToolCmd); 31 | artMock.registerArtifactToolRunnerMock(this.tmr); 32 | pkgMock.registerLocationHelpersMock(tmr); 33 | } 34 | 35 | public mockUniversalCommand( 36 | command: string, 37 | feed: string, 38 | packageName: string, 39 | packageVersion: string, 40 | path: string, 41 | result: TaskLibAnswerExecResult, 42 | service?: string 43 | ) { 44 | if (!service) { 45 | service = "https://example.visualstudio.com/defaultcollection"; 46 | } 47 | console.log( 48 | `${ 49 | this.artifactToolCmd 50 | } universal ${command} --feed ${feed} --service ${service} --package-name ${packageName} --package-version ${packageVersion} --path ${path} --patvar UNIVERSAL_${command.toUpperCase()}_PAT --verbosity verbose` 51 | ); 52 | this.answers.exec[ 53 | `${ 54 | this.artifactToolCmd 55 | } universal ${command} --feed ${feed} --service ${service} --package-name ${packageName} --package-version ${packageVersion} --path ${path} --patvar UNIVERSAL_${command.toUpperCase()}_PAT --verbosity verbose` 56 | ] = result; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/RestoreCacheCacheMiss.ts: -------------------------------------------------------------------------------- 1 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 5 | import { UniversalMockHelper } from "packaging-common/Tests/UniversalMockHelper"; 6 | import { Constants } from "./Constants"; 7 | 8 | const taskPath = path.join(__dirname, "..", "restorecache.js"); 9 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 10 | 11 | const a: TaskLibAnswers = { 12 | findMatch: { 13 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 14 | "**/*/node_modules": [] 15 | }, 16 | rmRF: { 17 | "/users/home/directory/tmp_cache": { success: true } 18 | }, 19 | checkPath: {}, 20 | exec: {}, 21 | exist: {}, 22 | which: {} 23 | }; 24 | 25 | tmr.setAnswers(a); 26 | 27 | const umh: UniversalMockHelper = new UniversalMockHelper( 28 | tmr, 29 | a, 30 | "/users/tmp/ArtifactTool.exe" 31 | ); 32 | 33 | umh.mockUniversalCommand( 34 | "download", 35 | "node-package-feed", 36 | "builddefinition1", 37 | `1.0.0-${process.platform}-${Constants.Hash}`, 38 | "/users/home/directory/tmp_cache", 39 | { 40 | code: 1, 41 | stdout: "ArtifactTool.exe output", 42 | stderr: "Can't find the package " 43 | } 44 | ); 45 | 46 | tmr.setInput("keyFile", "**/*/yarn.lock"); 47 | tmr.setInput("targetFolders", "**/*/node_modules"); 48 | 49 | // mock a specific module function called in task 50 | tmr.registerMock("fs", { 51 | readFileSync( 52 | path: string, 53 | options: 54 | | string 55 | | { 56 | encoding: string; 57 | flag?: string; 58 | } 59 | ): string { 60 | if (path.endsWith("/yarn.lock")) { 61 | const segments = path.split("/"); 62 | return segments.splice(segments.length - 3).join("/"); 63 | } 64 | return fs.readFileSync(path, options); 65 | }, 66 | chmodSync: fs.chmodSync, 67 | writeFileSync: fs.writeFileSync, 68 | readdirSync: fs.readdirSync, 69 | mkdirSync: fs.mkdirSync, 70 | copyFileSync: fs.copyFileSync, 71 | statSync: fs.statSync, 72 | linkSync: fs.linkSync, 73 | symlinkSync: fs.symlinkSync 74 | }); 75 | 76 | tmr.registerMock("shelljs", { 77 | exec(command: string) { 78 | console.log(`Mock executing command: ${command}`); 79 | return { 80 | code: 0, 81 | stdout: "shelljs output", 82 | stderr: null 83 | }; 84 | } 85 | }); 86 | 87 | tmr.run(); 88 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/RestoreCacheCacheHitCacheAlias.ts: -------------------------------------------------------------------------------- 1 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 5 | import { UniversalMockHelper } from "packaging-common/Tests/UniversalMockHelper"; 6 | import { Constants } from "./Constants"; 7 | 8 | const taskPath = path.join(__dirname, "..", "restorecache.js"); 9 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 10 | 11 | const a: TaskLibAnswers = { 12 | findMatch: { 13 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 14 | "**/*/node_modules": [] 15 | }, 16 | rmRF: { 17 | "/users/home/directory/tmp_cache": { success: true } 18 | }, 19 | checkPath: {}, 20 | exec: {}, 21 | exist: {}, 22 | which: {} 23 | }; 24 | 25 | tmr.setAnswers(a); 26 | 27 | const umh: UniversalMockHelper = new UniversalMockHelper( 28 | tmr, 29 | a, 30 | "/users/tmp/ArtifactTool.exe" 31 | ); 32 | 33 | umh.mockUniversalCommand( 34 | "download", 35 | "node-package-feed", 36 | "builddefinition1", 37 | `1.0.0-${process.platform}-${Constants.Hash}`, 38 | "/users/home/directory/tmp_cache", 39 | { 40 | code: 0, 41 | stdout: "ArtifactTool.exe output", 42 | stderr: "" 43 | } 44 | ); 45 | 46 | tmr.setInput("keyFile", "**/*/yarn.lock"); 47 | tmr.setInput("targetFolders", "**/*/node_modules"); 48 | tmr.setInput("alias", "Build"); 49 | 50 | // mock a specific module function called in task 51 | tmr.registerMock("fs", { 52 | readFileSync( 53 | path: string, 54 | options: 55 | | string 56 | | { 57 | encoding: string; 58 | flag?: string; 59 | } 60 | ): string { 61 | if (path.endsWith("/yarn.lock")) { 62 | const segments = path.split("/"); 63 | return segments.splice(segments.length - 3).join("/"); 64 | } 65 | return fs.readFileSync(path, options); 66 | }, 67 | chmodSync: fs.chmodSync, 68 | writeFileSync: fs.writeFileSync, 69 | readdirSync: fs.readdirSync, 70 | mkdirSync: fs.mkdirSync, 71 | copyFileSync: fs.copyFileSync, 72 | statSync: fs.statSync, 73 | linkSync: fs.linkSync, 74 | symlinkSync: fs.symlinkSync 75 | }); 76 | 77 | tmr.registerMock("shelljs", { 78 | exec(command: string) { 79 | console.log(`Mock executing command: ${command}`); 80 | return { 81 | code: 0, 82 | stdout: "shelljs output", 83 | stderr: null 84 | }; 85 | } 86 | }); 87 | 88 | tmr.run(); 89 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/RestoreCacheCacheHitPlatIndependent.ts: -------------------------------------------------------------------------------- 1 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 5 | import { UniversalMockHelper } from "packaging-common/Tests/UniversalMockHelper"; 6 | import { Constants } from "./Constants"; 7 | 8 | const taskPath = path.join(__dirname, "..", "restorecache.js"); 9 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 10 | 11 | const a: TaskLibAnswers = { 12 | findMatch: { 13 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 14 | "**/*/node_modules": [] 15 | }, 16 | rmRF: { 17 | "/users/home/directory/tmp_cache": { success: true } 18 | }, 19 | checkPath: {}, 20 | exec: {}, 21 | exist: {}, 22 | which: {} 23 | }; 24 | 25 | tmr.setAnswers(a); 26 | 27 | const umh: UniversalMockHelper = new UniversalMockHelper( 28 | tmr, 29 | a, 30 | "/users/tmp/ArtifactTool.exe" 31 | ); 32 | 33 | umh.mockUniversalCommand( 34 | "download", 35 | "node-package-feed", 36 | "builddefinition1", 37 | `1.0.0-${Constants.Hash}`, 38 | "/users/home/directory/tmp_cache", 39 | { 40 | code: 0, 41 | stdout: "ArtifactTool.exe output", 42 | stderr: "" 43 | } 44 | ); 45 | 46 | tmr.setInput("keyFile", "**/*/yarn.lock"); 47 | tmr.setInput("targetFolders", "**/*/node_modules"); 48 | tmr.setInput("platformIndependent", "true"); 49 | 50 | // mock a specific module function called in task 51 | tmr.registerMock("fs", { 52 | readFileSync( 53 | path: string, 54 | options: 55 | | string 56 | | { 57 | encoding: string; 58 | flag?: string; 59 | } 60 | ): string { 61 | if (path.endsWith("/yarn.lock")) { 62 | const segments = path.split("/"); 63 | return segments.splice(segments.length - 3).join("/"); 64 | } 65 | return fs.readFileSync(path, options); 66 | }, 67 | chmodSync: fs.chmodSync, 68 | writeFileSync: fs.writeFileSync, 69 | readdirSync: fs.readdirSync, 70 | mkdirSync: fs.mkdirSync, 71 | copyFileSync: fs.copyFileSync, 72 | statSync: fs.statSync, 73 | linkSync: fs.linkSync, 74 | symlinkSync: fs.symlinkSync 75 | }); 76 | 77 | tmr.registerMock("shelljs", { 78 | exec(command: string) { 79 | console.log(`Mock executing command: ${command}`); 80 | return { 81 | code: 0, 82 | stdout: "shelljs output", 83 | stderr: null 84 | }; 85 | } 86 | }); 87 | 88 | tmr.run(); 89 | -------------------------------------------------------------------------------- /ci/continuous-build.yml: -------------------------------------------------------------------------------- 1 | resources: 2 | - repo: self 3 | 4 | name: $(Version)$(Rev:.r) 5 | 6 | trigger: 7 | batch: true 8 | branches: 9 | include: 10 | - "master" 11 | 12 | pr: 13 | autoCancel: true 14 | branches: 15 | include: 16 | - "master" 17 | 18 | jobs: 19 | - job: BuildAndTest 20 | pool: 21 | vmImage: macOS 10.13 22 | demands: 23 | - npm 24 | - node.js 25 | 26 | steps: 27 | - task: NodeTool@0 28 | inputs: 29 | versionSpec: "8.12.0" 30 | 31 | - task: Npm@1 32 | displayName: "npm install" 33 | inputs: 34 | verbose: false 35 | 36 | - task: Gulp@0 37 | displayName: "gulp build" 38 | inputs: 39 | targets: build 40 | 41 | - task: Gulp@0 42 | displayName: "gulp test" 43 | inputs: 44 | targets: test 45 | arguments: "--testResults=TESTRESULTS.xml" 46 | 47 | - task: PublishTestResults@2 48 | displayName: "Publish Test Results **/TESTRESULTS.xml" 49 | inputs: 50 | testResultsFiles: "**/TESTRESULTS.xml" 51 | condition: succeededOrFailed() 52 | 53 | - job: Package 54 | dependsOn: BuildAndTest 55 | pool: 56 | vmImage: VS2017-Win2016 57 | demands: 58 | - npm 59 | - node.js 60 | 61 | steps: 62 | - task: NodeTool@0 63 | inputs: 64 | versionSpec: "8.12.0" 65 | 66 | - task: Npm@1 67 | displayName: "npm install" 68 | inputs: 69 | verbose: false 70 | 71 | - task: Gulp@0 72 | displayName: "gulp build" 73 | inputs: 74 | targets: build 75 | 76 | - task: ms-devlabs.vsts-developer-tools-build-tasks.tfx-installer-build-task.TfxInstaller@1 77 | displayName: "Use Node CLI for Azure DevOps (tfx-cli): v0.7.x" 78 | inputs: 79 | version: v0.7.x 80 | 81 | - task: ms-devlabs.vsts-developer-tools-build-tasks.package-extension-build-task.PackageVSTSExtension@1 82 | displayName: "Package Extension" 83 | inputs: 84 | extensionVersion: "$(Build.BuildNumber)" 85 | extensionVisibility: public 86 | updateTasksVersion: false 87 | 88 | - task: CopyFiles@2 89 | displayName: "Copy Files to staging" 90 | inputs: 91 | Contents: "**/*.vsix" 92 | TargetFolder: "$(Build.ArtifactStagingDirectory)" 93 | 94 | - task: PublishBuildArtifacts@1 95 | displayName: "Publish build artifacts" 96 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/RestoreCacheCacheMissDotKeyFile.ts: -------------------------------------------------------------------------------- 1 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 5 | import { UniversalMockHelper } from "packaging-common/Tests/UniversalMockHelper"; 6 | 7 | const taskPath = path.join(__dirname, "..", "restorecache.js"); 8 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 9 | 10 | const hash = "053a624fd3674f0969897b5c62a6c7debbac2b3a7b368c4e4bd9b583c69614db"; 11 | 12 | const a: TaskLibAnswers = { 13 | findMatch: { 14 | ".commit": [".commit"], 15 | "**/out-build": ["src/out/out-build"] 16 | }, 17 | rmRF: { 18 | "/users/home/directory/tmp_cache": { success: true } 19 | }, 20 | checkPath: {}, 21 | exec: {}, 22 | exist: {}, 23 | which: {} 24 | }; 25 | 26 | tmr.setAnswers(a); 27 | 28 | const umh: UniversalMockHelper = new UniversalMockHelper( 29 | tmr, 30 | a, 31 | "/users/tmp/ArtifactTool.exe" 32 | ); 33 | 34 | umh.mockUniversalCommand( 35 | "download", 36 | "node-package-feed", 37 | "builddefinition1", 38 | `1.0.0-${process.platform}-${hash}`, 39 | "/users/home/directory/tmp_cache", 40 | { 41 | code: 1, 42 | stdout: "ArtifactTool.exe output", 43 | stderr: "Can't find the package " 44 | } 45 | ); 46 | 47 | tmr.setInput("keyFile", ".commit"); 48 | tmr.setInput("targetFolder", "**/out-build"); 49 | 50 | // mock a specific module function called in task 51 | tmr.registerMock("fs", { 52 | readFileSync( 53 | path: string, 54 | options: 55 | | string 56 | | { 57 | encoding: string; 58 | flag?: string; 59 | } 60 | ): string { 61 | if (path.endsWith(".commit")) { 62 | console.log("PATH", path); 63 | 64 | const segments = path.split("/"); 65 | return segments.splice(segments.length - 1).join("/"); 66 | } 67 | return fs.readFileSync(path, options); 68 | }, 69 | chmodSync: fs.chmodSync, 70 | writeFileSync: fs.writeFileSync, 71 | readdirSync: fs.readdirSync, 72 | mkdirSync: fs.mkdirSync, 73 | copyFileSync: fs.copyFileSync, 74 | statSync: fs.statSync, 75 | linkSync: fs.linkSync, 76 | symlinkSync: fs.symlinkSync 77 | }); 78 | 79 | tmr.registerMock("shelljs", { 80 | exec(command: string) { 81 | console.log(`Mock executing command: ${command}`); 82 | return { 83 | code: 0, 84 | stdout: "shelljs output", 85 | stderr: null 86 | }; 87 | } 88 | }); 89 | 90 | tmr.run(); 91 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/RestoreCacheCacheMissCacheAlias.ts: -------------------------------------------------------------------------------- 1 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 5 | import { UniversalMockHelper } from "packaging-common/Tests/UniversalMockHelper"; 6 | import { Constants } from "./Constants"; 7 | 8 | const taskPath = path.join(__dirname, "..", "restorecache.js"); 9 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 10 | 11 | const a: TaskLibAnswers = { 12 | findMatch: { 13 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 14 | "**/*/node_modules": [] 15 | }, 16 | rmRF: { 17 | "/users/home/directory/tmp_cache": { success: true } 18 | }, 19 | checkPath: {}, 20 | exec: {}, 21 | exist: {}, 22 | which: {} 23 | }; 24 | 25 | tmr.setAnswers(a); 26 | 27 | const umh: UniversalMockHelper = new UniversalMockHelper( 28 | tmr, 29 | a, 30 | "/users/tmp/ArtifactTool.exe" 31 | ); 32 | 33 | umh.mockUniversalCommand( 34 | "download", 35 | "node-package-feed", 36 | "builddefinition1", 37 | `1.0.0-${process.platform}-${Constants.Hash}`, 38 | "/users/home/directory/tmp_cache", 39 | { 40 | code: 1, 41 | stdout: "ArtifactTool.exe output", 42 | stderr: "Can't find the package " 43 | } 44 | ); 45 | 46 | tmr.setInput("keyFile", "**/*/yarn.lock"); 47 | tmr.setInput("targetFolders", "**/*/node_modules"); 48 | tmr.setInput("alias", "Build"); 49 | 50 | // mock a specific module function called in task 51 | tmr.registerMock("fs", { 52 | readFileSync( 53 | path: string, 54 | options: 55 | | string 56 | | { 57 | encoding: string; 58 | flag?: string; 59 | } 60 | ): string { 61 | if (path.endsWith("/yarn.lock")) { 62 | const segments = path.split("/"); 63 | return segments.splice(segments.length - 3).join("/"); 64 | } 65 | return fs.readFileSync(path, options); 66 | }, 67 | chmodSync: fs.chmodSync, 68 | writeFileSync: fs.writeFileSync, 69 | readdirSync: fs.readdirSync, 70 | mkdirSync: fs.mkdirSync, 71 | copyFileSync: fs.copyFileSync, 72 | statSync: fs.statSync, 73 | linkSync: fs.linkSync, 74 | symlinkSync: fs.symlinkSync 75 | }); 76 | 77 | tmr.registerMock("shelljs", { 78 | exec(command: string) { 79 | console.log(`Mock executing command: ${command}`); 80 | return { 81 | code: 0, 82 | stdout: "shelljs output", 83 | stderr: null 84 | }; 85 | } 86 | }); 87 | 88 | tmr.run(); 89 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/RestoreCachePermissionsError.ts: -------------------------------------------------------------------------------- 1 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 5 | import { UniversalMockHelper } from "packaging-common/Tests/UniversalMockHelper"; 6 | import { Constants } from "./Constants"; 7 | 8 | const taskPath = path.join(__dirname, "..", "restorecache.js"); 9 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 10 | 11 | const a: TaskLibAnswers = { 12 | findMatch: { 13 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 14 | "**/*/node_modules": [] 15 | }, 16 | rmRF: { 17 | "/users/home/directory/tmp_cache": { success: true } 18 | }, 19 | checkPath: {}, 20 | exec: {}, 21 | exist: {}, 22 | which: {} 23 | }; 24 | 25 | tmr.setAnswers(a); 26 | 27 | const umh: UniversalMockHelper = new UniversalMockHelper( 28 | tmr, 29 | a, 30 | "/users/tmp/ArtifactTool.exe" 31 | ); 32 | 33 | umh.mockUniversalCommand( 34 | "download", 35 | "node-package-feed", 36 | "builddefinition1", 37 | `1.0.0-${process.platform}-${Constants.Hash}`, 38 | "/users/home/directory/tmp_cache", 39 | { 40 | code: 1, 41 | stdout: "ArtifactTool.exe output", 42 | stderr: 43 | "An error occurred on the service. User lacks permission to complete this action." 44 | } 45 | ); 46 | 47 | tmr.setInput("keyFile", "**/*/yarn.lock"); 48 | tmr.setInput("targetFolders", "**/*/node_modules"); 49 | 50 | // mock a specific module function called in task 51 | tmr.registerMock("fs", { 52 | readFileSync( 53 | path: string, 54 | options: 55 | | string 56 | | { 57 | encoding: string; 58 | flag?: string; 59 | } 60 | ): string { 61 | if (path.endsWith("/yarn.lock")) { 62 | const segments = path.split("/"); 63 | return segments.splice(segments.length - 3).join("/"); 64 | } 65 | return fs.readFileSync(path, options); 66 | }, 67 | chmodSync: fs.chmodSync, 68 | writeFileSync: fs.writeFileSync, 69 | readdirSync: fs.readdirSync, 70 | mkdirSync: fs.mkdirSync, 71 | copyFileSync: fs.copyFileSync, 72 | statSync: fs.statSync, 73 | linkSync: fs.linkSync, 74 | symlinkSync: fs.symlinkSync 75 | }); 76 | 77 | tmr.registerMock("shelljs", { 78 | exec(command: string) { 79 | console.log(`Mock executing command: ${command}`); 80 | return { 81 | code: 0, 82 | stdout: "shelljs output", 83 | stderr: null 84 | }; 85 | } 86 | }); 87 | 88 | tmr.run(); 89 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/ArtifactToolRunner.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as tl from "azure-pipelines-task-lib"; 5 | const fs = require("fs"); 6 | const os = require("os"); 7 | import child = require("child_process"); 8 | import stream = require("stream"); 9 | import {IExecOptions, IExecSyncResult} from "azure-pipelines-task-lib/toolrunner"; 10 | 11 | export interface IArtifactToolOptions { 12 | artifactToolPath: string; 13 | feedId: string; 14 | accountUrl: string; 15 | packageName: string; 16 | packageVersion: string; 17 | publishedPackageVar: string; 18 | } 19 | 20 | export function getOptions(): IExecOptions { 21 | const result: IExecOptions = { 22 | cwd: process.cwd(), 23 | env: Object.assign({}, process.env), 24 | silent: false, 25 | failOnStdErr: false, 26 | ignoreReturnCode: false, 27 | windowsVerbatimArguments: false, 28 | } as IExecOptions; 29 | result.outStream = process.stdout as stream.Writable; 30 | result.errStream = process.stderr as stream.Writable; 31 | return result; 32 | } 33 | 34 | function getCommandString(toolPath: string, command: string[]) { 35 | let cmd: string = toolPath; 36 | command.forEach((a: string): void => { 37 | cmd += ` ${a}`; 38 | }); 39 | return cmd; 40 | } 41 | 42 | export function runArtifactTool(artifactToolPath: string, command: string[], execOptions: IExecOptions): IExecSyncResult { 43 | 44 | if (tl.osType() === "Windows_NT" || artifactToolPath.trim().toLowerCase().endsWith(".exe")) { 45 | return tl.execSync(artifactToolPath, command, execOptions); 46 | } else { 47 | fs.chmodSync(artifactToolPath, "755"); 48 | if (!execOptions.silent) { 49 | execOptions.outStream.write(getCommandString(artifactToolPath, command) + os.EOL); 50 | } 51 | 52 | const result = child.spawnSync(artifactToolPath, command, execOptions); 53 | 54 | if (!execOptions.silent && result.stdout && result.stdout.length > 0) { 55 | execOptions.outStream.write(result.stdout); 56 | } 57 | 58 | if (!execOptions.silent && result.stderr && result.stderr.length > 0) { 59 | execOptions.errStream.write(result.stderr); 60 | } 61 | 62 | const res: IExecSyncResult = { code: result.status, error: result.error } as IExecSyncResult; 63 | res.stdout = (result.stdout) ? result.stdout.toString() : null; 64 | res.stderr = (result.stderr) ? result.stderr.toString() : null; 65 | return res; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/cache/universalPackages.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as tl from "azure-pipelines-task-lib"; 5 | import * as pkgLocationUtils from "../locationUtilities"; 6 | import * as artifactToolUtilities from "../ArtifactToolUtilities"; 7 | import * as universalDownload from "./universaldownload"; 8 | import * as universalPublish from "./universalpublish"; 9 | 10 | export interface UniversalPackagesResult { 11 | toolRan: boolean; 12 | success: boolean; 13 | } 14 | 15 | export class UniversalPackages { 16 | private artifactToolPath: string; 17 | 18 | public download = async function( 19 | hash: string, 20 | targetFolder: string 21 | ): Promise { 22 | if (!this.artifactToolPath) { 23 | const initialized = await this.init(); 24 | if (!initialized) { 25 | console.log("Error initializing artifact tool utility"); 26 | return { 27 | toolRan: false, 28 | success: false, 29 | }; 30 | } 31 | } 32 | return universalDownload.run(this.artifactToolPath, hash, targetFolder); 33 | }; 34 | 35 | public publish = async function( 36 | hash: string, 37 | targetFolder: string 38 | ): Promise { 39 | if (!this.artifactToolPath) { 40 | const initialized = await this.init(); 41 | if (!initialized) { 42 | console.log("Error initializing artifact tool utility"); 43 | return { 44 | toolRan: false, 45 | success: false, 46 | }; 47 | } 48 | } 49 | return universalPublish.run(this.artifactToolPath, hash, targetFolder); 50 | }; 51 | 52 | private init = async function main( 53 | command: string, 54 | hash: string, 55 | targetFolder: string 56 | ): Promise { 57 | // Getting artifact tool 58 | tl.debug("Getting artifact tool"); 59 | 60 | try { 61 | const localAccessToken = pkgLocationUtils.getSystemAccessToken(); 62 | const serviceUri = tl.getEndpointUrl("SYSTEMVSSCONNECTION", false); 63 | const blobUri = await pkgLocationUtils.getBlobstoreUriFromBaseServiceUri( 64 | serviceUri, 65 | localAccessToken 66 | ); 67 | console.log(blobUri); 68 | 69 | // Finding the artifact tool directory 70 | this.artifactToolPath = await artifactToolUtilities.getArtifactToolFromService( 71 | blobUri, 72 | localAccessToken, 73 | "artifacttool" 74 | ); 75 | 76 | if (!this.artifactToolPath) { 77 | return false; 78 | } 79 | 80 | console.log(this.artifactToolPath); 81 | } catch (error) { 82 | console.log(error); 83 | return false; 84 | } 85 | return true; 86 | }; 87 | } 88 | -------------------------------------------------------------------------------- /images/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | icon 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/module.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": { 3 | "Error_ApiKeyNotSupported": "DotNetCore currently does not support using an encrypted Api Key.", 4 | "Error_ExpectedConfigurationElement": "Invalid xml. Expected element named 'configuration'.", 5 | "Error_NoMatchingFilesFoundForPattern": "No matching files were found with search pattern: %s", 6 | "Error_NoUrlWasFoundWhichMatches": "No download URL was found for %s", 7 | "Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 8 | "Error_NuGetToolInstallerFailer": "Tool install failed: %s", 9 | "Info_AvailableVersions": "The available versions are: %s", 10 | "Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", 11 | "Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", 12 | "Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 13 | "Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", 14 | "Info_UsingToolPath": "Using tool path: %s", 15 | "Info_UsingVersion": "Using version: %s", 16 | "NGCommon_AddingSources": "Setting credentials in NuGet.config", 17 | "NGCommon_AreaNotFoundInSps": "Unable to locate the '%s' [%s] area. The service containing that area may not be available in your region.", 18 | "NGCommon_DetectedNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable): %s", 19 | "NGCommon_DetectedNuGetVersion": "Detected NuGet version %s / %s", 20 | "NGCommon_IgnoringNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable). Extensions are ignored when using the built-in NuGet client", 21 | "NGCommon_NoSourcesFoundInConfig": "No package sources were found in the NuGet.config file at %s", 22 | "NGCommon_NuGetConfigIsInvalid": "The NuGet.config at %s is invalid.", 23 | "NGCommon_NuGetConfigIsPackagesConfig": "Expected a NuGet.config file at %s, but its contents appear to be a packages.config. Check the settings for the %s task and confirm you selected NuGet.config rather than packages.config.", 24 | "NGCommon_RemovingSources": "Preparing to set credentials in NuGet.config", 25 | "NGCommon_SpsNotFound": "Unable to find the '%s' [%s] area. There may be a problem with your Team Foundation Server installation.", 26 | "NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.", 27 | "NGCommon_UnableToFindTool": "Unable to find tool %s", 28 | "Warning_SessionCreationFailed": "Could not create provenance session: %s", 29 | "Warning_UpdatingNuGetVersion": "Updating version of NuGet.exe to %s from %s. Behavior changes or breaking changes might occur as NuGet updates to a new version. If this is not desired, uncheck the 'Check for Latest Version' option in the task." 30 | } 31 | } -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/SaveCacheCacheMissDotKeyFile.ts: -------------------------------------------------------------------------------- 1 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 5 | import { UniversalMockHelper } from "packaging-common/Tests/UniversalMockHelper"; 6 | 7 | const taskPath = path.join(__dirname, "..", "savecache.js"); 8 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 9 | const hash = "053a624fd3674f0969897b5c62a6c7debbac2b3a7b368c4e4bd9b583c69614db"; 10 | 11 | // provide answers for task mock 12 | const a: TaskLibAnswers = { 13 | findMatch: { 14 | ".commit": [".commit"], 15 | "**/out-build": ["src/out/out-build"] 16 | }, 17 | find: { 18 | DefaultWorkingDirectory: ["src/out/out-build", ".commit", ".commitfoo/bar"] 19 | }, 20 | rmRF: { 21 | "/users/home/DefaultWorkingDirectory/tmp_cache": { success: true }, 22 | "DefaultWorkingDirectory/tmp_cache": { success: true }, 23 | '"DefaultWorkingDirectory/tmp_cache"': { success: true } 24 | }, 25 | stats: { 26 | "src/out/out-build": { 27 | isDirectory() { 28 | return true; 29 | } 30 | } 31 | }, 32 | exist: { 33 | "DefaultWorkingDirectory/tmp_cache": true 34 | }, 35 | checkPath: {}, 36 | exec: {}, 37 | which: {} 38 | }; 39 | 40 | tmr.setAnswers(a); 41 | 42 | const umh: UniversalMockHelper = new UniversalMockHelper( 43 | tmr, 44 | a, 45 | "/users/tmp/ArtifactTool.exe" 46 | ); 47 | 48 | umh.mockUniversalCommand( 49 | "publish", 50 | "node-package-feed", 51 | "builddefinition1", 52 | `1.0.0-${process.platform}-${hash}`, 53 | "DefaultWorkingDirectory/tmp_cache", 54 | { 55 | code: 0, 56 | stdout: "ArtifactTool.exe output", 57 | stderr: "" 58 | } 59 | ); 60 | 61 | tmr.setInput("keyFile", ".commit"); 62 | tmr.setInput("targetFolder", "**/out-build"); 63 | 64 | const key = `${process.platform}-${hash}`.toUpperCase(); 65 | process.env[key] = "false"; 66 | process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = "DefaultWorkingDirectory"; 67 | 68 | // mock a specific module function called in task 69 | tmr.registerMock("fs", { 70 | readFileSync( 71 | path: string, 72 | options: 73 | | string 74 | | { 75 | encoding: string; 76 | flag?: string; 77 | } 78 | ): string { 79 | if (path.endsWith(".commit")) { 80 | const segments = path.split("/"); 81 | return segments.splice(segments.length - 1).join("/"); 82 | } 83 | return fs.readFileSync(path, options); 84 | }, 85 | chmodSync: fs.chmodSync, 86 | writeFileSync: fs.writeFileSync, 87 | readdirSync: fs.readdirSync, 88 | mkdirSync: fs.mkdirSync, 89 | copyFileSync: fs.copyFileSync, 90 | statSync: fs.statSync, 91 | linkSync: fs.linkSync, 92 | symlinkSync: fs.symlinkSync 93 | }); 94 | 95 | tmr.registerMock("shelljs", { 96 | exec(command: string) { 97 | console.log(`Mock exec: ${command}`); 98 | return { 99 | code: 0, 100 | stdout: "shelljs output", 101 | stderr: null 102 | }; 103 | } 104 | }); 105 | 106 | tmr.run(); 107 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Strings/resources.resjson/de-de/resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "loc.messages.Error_ApiKeyNotSupported": "DotNetCore currently does not support using an encrypted Api Key.", 3 | "loc.messages.Error_ExpectedConfigurationElement": "Invalid xml. Expected element named 'configuration'.", 4 | "loc.messages.Error_NoMatchingFilesFoundForPattern": "No matching files were found with search pattern: %s", 5 | "loc.messages.Error_NoUrlWasFoundWhichMatches": "No download URL was found for %s", 6 | "loc.messages.Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 7 | "loc.messages.Error_NuGetToolInstallerFailer": "Tool install failed: %s", 8 | "loc.messages.Info_AvailableVersions": "The available versions are: %s", 9 | "loc.messages.Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", 10 | "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", 11 | "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 12 | "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", 13 | "loc.messages.Info_UsingToolPath": "Using tool path: %s", 14 | "loc.messages.Info_UsingVersion": "Using version: %s", 15 | "loc.messages.NGCommon_AddingSources": "Setting credentials in NuGet.config", 16 | "loc.messages.NGCommon_AreaNotFoundInSps": "Unable to locate the '%s' [%s] area. The service containing that area may not be available in your region.", 17 | "loc.messages.NGCommon_DetectedNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable): %s", 18 | "loc.messages.NGCommon_DetectedNuGetVersion": "Detected NuGet version %s / %s", 19 | "loc.messages.NGCommon_IgnoringNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable). Extensions are ignored when using the built-in NuGet client", 20 | "loc.messages.NGCommon_NoSourcesFoundInConfig": "No package sources were found in the NuGet.config file at %s", 21 | "loc.messages.NGCommon_NuGetConfigIsInvalid": "The NuGet.config at %s is invalid.", 22 | "loc.messages.NGCommon_NuGetConfigIsPackagesConfig": "Expected a NuGet.config file at %s, but its contents appear to be a packages.config. Check the settings for the %s task and confirm you selected NuGet.config rather than packages.config.", 23 | "loc.messages.NGCommon_RemovingSources": "Preparing to set credentials in NuGet.config", 24 | "loc.messages.NGCommon_SpsNotFound": "Unable to find the '%s' [%s] area. There may be a problem with your Team Foundation Server installation.", 25 | "loc.messages.NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.", 26 | "loc.messages.NGCommon_UnableToFindTool": "Unable to find tool %s", 27 | "loc.messages.Warning_SessionCreationFailed": "Could not create provenance session: %s", 28 | "loc.messages.Warning_UpdatingNuGetVersion": "Updating version of NuGet.exe to %s from %s. Behavior changes or breaking changes might occur as NuGet updates to a new version. If this is not desired, uncheck the 'Check for Latest Version' option in the task." 29 | } -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Strings/resources.resjson/en-US/resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "loc.messages.Error_ApiKeyNotSupported": "DotNetCore currently does not support using an encrypted Api Key.", 3 | "loc.messages.Error_ExpectedConfigurationElement": "Invalid xml. Expected element named 'configuration'.", 4 | "loc.messages.Error_NoMatchingFilesFoundForPattern": "No matching files were found with search pattern: %s", 5 | "loc.messages.Error_NoUrlWasFoundWhichMatches": "No download URL was found for %s", 6 | "loc.messages.Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 7 | "loc.messages.Error_NuGetToolInstallerFailer": "Tool install failed: %s", 8 | "loc.messages.Info_AvailableVersions": "The available versions are: %s", 9 | "loc.messages.Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", 10 | "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", 11 | "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 12 | "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", 13 | "loc.messages.Info_UsingToolPath": "Using tool path: %s", 14 | "loc.messages.Info_UsingVersion": "Using version: %s", 15 | "loc.messages.NGCommon_AddingSources": "Setting credentials in NuGet.config", 16 | "loc.messages.NGCommon_AreaNotFoundInSps": "Unable to locate the '%s' [%s] area. The service containing that area may not be available in your region.", 17 | "loc.messages.NGCommon_DetectedNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable): %s", 18 | "loc.messages.NGCommon_DetectedNuGetVersion": "Detected NuGet version %s / %s", 19 | "loc.messages.NGCommon_IgnoringNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable). Extensions are ignored when using the built-in NuGet client", 20 | "loc.messages.NGCommon_NoSourcesFoundInConfig": "No package sources were found in the NuGet.config file at %s", 21 | "loc.messages.NGCommon_NuGetConfigIsInvalid": "The NuGet.config at %s is invalid.", 22 | "loc.messages.NGCommon_NuGetConfigIsPackagesConfig": "Expected a NuGet.config file at %s, but its contents appear to be a packages.config. Check the settings for the %s task and confirm you selected NuGet.config rather than packages.config.", 23 | "loc.messages.NGCommon_RemovingSources": "Preparing to set credentials in NuGet.config", 24 | "loc.messages.NGCommon_SpsNotFound": "Unable to find the '%s' [%s] area. There may be a problem with your Team Foundation Server installation.", 25 | "loc.messages.NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.", 26 | "loc.messages.NGCommon_UnableToFindTool": "Unable to find tool %s", 27 | "loc.messages.Warning_SessionCreationFailed": "Could not create provenance session: %s", 28 | "loc.messages.Warning_UpdatingNuGetVersion": "Updating version of NuGet.exe to %s from %s. Behavior changes or breaking changes might occur as NuGet updates to a new version. If this is not desired, uncheck the 'Check for Latest Version' option in the task." 29 | } -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Strings/resources.resjson/es-es/resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "loc.messages.Error_ApiKeyNotSupported": "DotNetCore currently does not support using an encrypted Api Key.", 3 | "loc.messages.Error_ExpectedConfigurationElement": "Invalid xml. Expected element named 'configuration'.", 4 | "loc.messages.Error_NoMatchingFilesFoundForPattern": "No matching files were found with search pattern: %s", 5 | "loc.messages.Error_NoUrlWasFoundWhichMatches": "No download URL was found for %s", 6 | "loc.messages.Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 7 | "loc.messages.Error_NuGetToolInstallerFailer": "Tool install failed: %s", 8 | "loc.messages.Info_AvailableVersions": "The available versions are: %s", 9 | "loc.messages.Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", 10 | "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", 11 | "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 12 | "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", 13 | "loc.messages.Info_UsingToolPath": "Using tool path: %s", 14 | "loc.messages.Info_UsingVersion": "Using version: %s", 15 | "loc.messages.NGCommon_AddingSources": "Setting credentials in NuGet.config", 16 | "loc.messages.NGCommon_AreaNotFoundInSps": "Unable to locate the '%s' [%s] area. The service containing that area may not be available in your region.", 17 | "loc.messages.NGCommon_DetectedNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable): %s", 18 | "loc.messages.NGCommon_DetectedNuGetVersion": "Detected NuGet version %s / %s", 19 | "loc.messages.NGCommon_IgnoringNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable). Extensions are ignored when using the built-in NuGet client", 20 | "loc.messages.NGCommon_NoSourcesFoundInConfig": "No package sources were found in the NuGet.config file at %s", 21 | "loc.messages.NGCommon_NuGetConfigIsInvalid": "The NuGet.config at %s is invalid.", 22 | "loc.messages.NGCommon_NuGetConfigIsPackagesConfig": "Expected a NuGet.config file at %s, but its contents appear to be a packages.config. Check the settings for the %s task and confirm you selected NuGet.config rather than packages.config.", 23 | "loc.messages.NGCommon_RemovingSources": "Preparing to set credentials in NuGet.config", 24 | "loc.messages.NGCommon_SpsNotFound": "Unable to find the '%s' [%s] area. There may be a problem with your Team Foundation Server installation.", 25 | "loc.messages.NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.", 26 | "loc.messages.NGCommon_UnableToFindTool": "Unable to find tool %s", 27 | "loc.messages.Warning_SessionCreationFailed": "Could not create provenance session: %s", 28 | "loc.messages.Warning_UpdatingNuGetVersion": "Updating version of NuGet.exe to %s from %s. Behavior changes or breaking changes might occur as NuGet updates to a new version. If this is not desired, uncheck the 'Check for Latest Version' option in the task." 29 | } -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Strings/resources.resjson/fr-fr/resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "loc.messages.Error_ApiKeyNotSupported": "DotNetCore currently does not support using an encrypted Api Key.", 3 | "loc.messages.Error_ExpectedConfigurationElement": "Invalid xml. Expected element named 'configuration'.", 4 | "loc.messages.Error_NoMatchingFilesFoundForPattern": "No matching files were found with search pattern: %s", 5 | "loc.messages.Error_NoUrlWasFoundWhichMatches": "No download URL was found for %s", 6 | "loc.messages.Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 7 | "loc.messages.Error_NuGetToolInstallerFailer": "Tool install failed: %s", 8 | "loc.messages.Info_AvailableVersions": "The available versions are: %s", 9 | "loc.messages.Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", 10 | "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", 11 | "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 12 | "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", 13 | "loc.messages.Info_UsingToolPath": "Using tool path: %s", 14 | "loc.messages.Info_UsingVersion": "Using version: %s", 15 | "loc.messages.NGCommon_AddingSources": "Setting credentials in NuGet.config", 16 | "loc.messages.NGCommon_AreaNotFoundInSps": "Unable to locate the '%s' [%s] area. The service containing that area may not be available in your region.", 17 | "loc.messages.NGCommon_DetectedNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable): %s", 18 | "loc.messages.NGCommon_DetectedNuGetVersion": "Detected NuGet version %s / %s", 19 | "loc.messages.NGCommon_IgnoringNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable). Extensions are ignored when using the built-in NuGet client", 20 | "loc.messages.NGCommon_NoSourcesFoundInConfig": "No package sources were found in the NuGet.config file at %s", 21 | "loc.messages.NGCommon_NuGetConfigIsInvalid": "The NuGet.config at %s is invalid.", 22 | "loc.messages.NGCommon_NuGetConfigIsPackagesConfig": "Expected a NuGet.config file at %s, but its contents appear to be a packages.config. Check the settings for the %s task and confirm you selected NuGet.config rather than packages.config.", 23 | "loc.messages.NGCommon_RemovingSources": "Preparing to set credentials in NuGet.config", 24 | "loc.messages.NGCommon_SpsNotFound": "Unable to find the '%s' [%s] area. There may be a problem with your Team Foundation Server installation.", 25 | "loc.messages.NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.", 26 | "loc.messages.NGCommon_UnableToFindTool": "Unable to find tool %s", 27 | "loc.messages.Warning_SessionCreationFailed": "Could not create provenance session: %s", 28 | "loc.messages.Warning_UpdatingNuGetVersion": "Updating version of NuGet.exe to %s from %s. Behavior changes or breaking changes might occur as NuGet updates to a new version. If this is not desired, uncheck the 'Check for Latest Version' option in the task." 29 | } -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Strings/resources.resjson/it-IT/resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "loc.messages.Error_ApiKeyNotSupported": "DotNetCore currently does not support using an encrypted Api Key.", 3 | "loc.messages.Error_ExpectedConfigurationElement": "Invalid xml. Expected element named 'configuration'.", 4 | "loc.messages.Error_NoMatchingFilesFoundForPattern": "No matching files were found with search pattern: %s", 5 | "loc.messages.Error_NoUrlWasFoundWhichMatches": "No download URL was found for %s", 6 | "loc.messages.Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 7 | "loc.messages.Error_NuGetToolInstallerFailer": "Tool install failed: %s", 8 | "loc.messages.Info_AvailableVersions": "The available versions are: %s", 9 | "loc.messages.Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", 10 | "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", 11 | "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 12 | "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", 13 | "loc.messages.Info_UsingToolPath": "Using tool path: %s", 14 | "loc.messages.Info_UsingVersion": "Using version: %s", 15 | "loc.messages.NGCommon_AddingSources": "Setting credentials in NuGet.config", 16 | "loc.messages.NGCommon_AreaNotFoundInSps": "Unable to locate the '%s' [%s] area. The service containing that area may not be available in your region.", 17 | "loc.messages.NGCommon_DetectedNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable): %s", 18 | "loc.messages.NGCommon_DetectedNuGetVersion": "Detected NuGet version %s / %s", 19 | "loc.messages.NGCommon_IgnoringNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable). Extensions are ignored when using the built-in NuGet client", 20 | "loc.messages.NGCommon_NoSourcesFoundInConfig": "No package sources were found in the NuGet.config file at %s", 21 | "loc.messages.NGCommon_NuGetConfigIsInvalid": "The NuGet.config at %s is invalid.", 22 | "loc.messages.NGCommon_NuGetConfigIsPackagesConfig": "Expected a NuGet.config file at %s, but its contents appear to be a packages.config. Check the settings for the %s task and confirm you selected NuGet.config rather than packages.config.", 23 | "loc.messages.NGCommon_RemovingSources": "Preparing to set credentials in NuGet.config", 24 | "loc.messages.NGCommon_SpsNotFound": "Unable to find the '%s' [%s] area. There may be a problem with your Team Foundation Server installation.", 25 | "loc.messages.NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.", 26 | "loc.messages.NGCommon_UnableToFindTool": "Unable to find tool %s", 27 | "loc.messages.Warning_SessionCreationFailed": "Could not create provenance session: %s", 28 | "loc.messages.Warning_UpdatingNuGetVersion": "Updating version of NuGet.exe to %s from %s. Behavior changes or breaking changes might occur as NuGet updates to a new version. If this is not desired, uncheck the 'Check for Latest Version' option in the task." 29 | } -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Strings/resources.resjson/ja-jp/resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "loc.messages.Error_ApiKeyNotSupported": "DotNetCore currently does not support using an encrypted Api Key.", 3 | "loc.messages.Error_ExpectedConfigurationElement": "Invalid xml. Expected element named 'configuration'.", 4 | "loc.messages.Error_NoMatchingFilesFoundForPattern": "No matching files were found with search pattern: %s", 5 | "loc.messages.Error_NoUrlWasFoundWhichMatches": "No download URL was found for %s", 6 | "loc.messages.Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 7 | "loc.messages.Error_NuGetToolInstallerFailer": "Tool install failed: %s", 8 | "loc.messages.Info_AvailableVersions": "The available versions are: %s", 9 | "loc.messages.Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", 10 | "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", 11 | "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 12 | "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", 13 | "loc.messages.Info_UsingToolPath": "Using tool path: %s", 14 | "loc.messages.Info_UsingVersion": "Using version: %s", 15 | "loc.messages.NGCommon_AddingSources": "Setting credentials in NuGet.config", 16 | "loc.messages.NGCommon_AreaNotFoundInSps": "Unable to locate the '%s' [%s] area. The service containing that area may not be available in your region.", 17 | "loc.messages.NGCommon_DetectedNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable): %s", 18 | "loc.messages.NGCommon_DetectedNuGetVersion": "Detected NuGet version %s / %s", 19 | "loc.messages.NGCommon_IgnoringNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable). Extensions are ignored when using the built-in NuGet client", 20 | "loc.messages.NGCommon_NoSourcesFoundInConfig": "No package sources were found in the NuGet.config file at %s", 21 | "loc.messages.NGCommon_NuGetConfigIsInvalid": "The NuGet.config at %s is invalid.", 22 | "loc.messages.NGCommon_NuGetConfigIsPackagesConfig": "Expected a NuGet.config file at %s, but its contents appear to be a packages.config. Check the settings for the %s task and confirm you selected NuGet.config rather than packages.config.", 23 | "loc.messages.NGCommon_RemovingSources": "Preparing to set credentials in NuGet.config", 24 | "loc.messages.NGCommon_SpsNotFound": "Unable to find the '%s' [%s] area. There may be a problem with your Team Foundation Server installation.", 25 | "loc.messages.NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.", 26 | "loc.messages.NGCommon_UnableToFindTool": "Unable to find tool %s", 27 | "loc.messages.Warning_SessionCreationFailed": "Could not create provenance session: %s", 28 | "loc.messages.Warning_UpdatingNuGetVersion": "Updating version of NuGet.exe to %s from %s. Behavior changes or breaking changes might occur as NuGet updates to a new version. If this is not desired, uncheck the 'Check for Latest Version' option in the task." 29 | } -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Strings/resources.resjson/ko-KR/resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "loc.messages.Error_ApiKeyNotSupported": "DotNetCore currently does not support using an encrypted Api Key.", 3 | "loc.messages.Error_ExpectedConfigurationElement": "Invalid xml. Expected element named 'configuration'.", 4 | "loc.messages.Error_NoMatchingFilesFoundForPattern": "No matching files were found with search pattern: %s", 5 | "loc.messages.Error_NoUrlWasFoundWhichMatches": "No download URL was found for %s", 6 | "loc.messages.Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 7 | "loc.messages.Error_NuGetToolInstallerFailer": "Tool install failed: %s", 8 | "loc.messages.Info_AvailableVersions": "The available versions are: %s", 9 | "loc.messages.Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", 10 | "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", 11 | "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 12 | "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", 13 | "loc.messages.Info_UsingToolPath": "Using tool path: %s", 14 | "loc.messages.Info_UsingVersion": "Using version: %s", 15 | "loc.messages.NGCommon_AddingSources": "Setting credentials in NuGet.config", 16 | "loc.messages.NGCommon_AreaNotFoundInSps": "Unable to locate the '%s' [%s] area. The service containing that area may not be available in your region.", 17 | "loc.messages.NGCommon_DetectedNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable): %s", 18 | "loc.messages.NGCommon_DetectedNuGetVersion": "Detected NuGet version %s / %s", 19 | "loc.messages.NGCommon_IgnoringNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable). Extensions are ignored when using the built-in NuGet client", 20 | "loc.messages.NGCommon_NoSourcesFoundInConfig": "No package sources were found in the NuGet.config file at %s", 21 | "loc.messages.NGCommon_NuGetConfigIsInvalid": "The NuGet.config at %s is invalid.", 22 | "loc.messages.NGCommon_NuGetConfigIsPackagesConfig": "Expected a NuGet.config file at %s, but its contents appear to be a packages.config. Check the settings for the %s task and confirm you selected NuGet.config rather than packages.config.", 23 | "loc.messages.NGCommon_RemovingSources": "Preparing to set credentials in NuGet.config", 24 | "loc.messages.NGCommon_SpsNotFound": "Unable to find the '%s' [%s] area. There may be a problem with your Team Foundation Server installation.", 25 | "loc.messages.NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.", 26 | "loc.messages.NGCommon_UnableToFindTool": "Unable to find tool %s", 27 | "loc.messages.Warning_SessionCreationFailed": "Could not create provenance session: %s", 28 | "loc.messages.Warning_UpdatingNuGetVersion": "Updating version of NuGet.exe to %s from %s. Behavior changes or breaking changes might occur as NuGet updates to a new version. If this is not desired, uncheck the 'Check for Latest Version' option in the task." 29 | } -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Strings/resources.resjson/ru-RU/resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "loc.messages.Error_ApiKeyNotSupported": "DotNetCore currently does not support using an encrypted Api Key.", 3 | "loc.messages.Error_ExpectedConfigurationElement": "Invalid xml. Expected element named 'configuration'.", 4 | "loc.messages.Error_NoMatchingFilesFoundForPattern": "No matching files were found with search pattern: %s", 5 | "loc.messages.Error_NoUrlWasFoundWhichMatches": "No download URL was found for %s", 6 | "loc.messages.Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 7 | "loc.messages.Error_NuGetToolInstallerFailer": "Tool install failed: %s", 8 | "loc.messages.Info_AvailableVersions": "The available versions are: %s", 9 | "loc.messages.Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", 10 | "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", 11 | "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 12 | "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", 13 | "loc.messages.Info_UsingToolPath": "Using tool path: %s", 14 | "loc.messages.Info_UsingVersion": "Using version: %s", 15 | "loc.messages.NGCommon_AddingSources": "Setting credentials in NuGet.config", 16 | "loc.messages.NGCommon_AreaNotFoundInSps": "Unable to locate the '%s' [%s] area. The service containing that area may not be available in your region.", 17 | "loc.messages.NGCommon_DetectedNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable): %s", 18 | "loc.messages.NGCommon_DetectedNuGetVersion": "Detected NuGet version %s / %s", 19 | "loc.messages.NGCommon_IgnoringNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable). Extensions are ignored when using the built-in NuGet client", 20 | "loc.messages.NGCommon_NoSourcesFoundInConfig": "No package sources were found in the NuGet.config file at %s", 21 | "loc.messages.NGCommon_NuGetConfigIsInvalid": "The NuGet.config at %s is invalid.", 22 | "loc.messages.NGCommon_NuGetConfigIsPackagesConfig": "Expected a NuGet.config file at %s, but its contents appear to be a packages.config. Check the settings for the %s task and confirm you selected NuGet.config rather than packages.config.", 23 | "loc.messages.NGCommon_RemovingSources": "Preparing to set credentials in NuGet.config", 24 | "loc.messages.NGCommon_SpsNotFound": "Unable to find the '%s' [%s] area. There may be a problem with your Team Foundation Server installation.", 25 | "loc.messages.NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.", 26 | "loc.messages.NGCommon_UnableToFindTool": "Unable to find tool %s", 27 | "loc.messages.Warning_SessionCreationFailed": "Could not create provenance session: %s", 28 | "loc.messages.Warning_UpdatingNuGetVersion": "Updating version of NuGet.exe to %s from %s. Behavior changes or breaking changes might occur as NuGet updates to a new version. If this is not desired, uncheck the 'Check for Latest Version' option in the task." 29 | } -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Strings/resources.resjson/zh-CN/resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "loc.messages.Error_ApiKeyNotSupported": "DotNetCore currently does not support using an encrypted Api Key.", 3 | "loc.messages.Error_ExpectedConfigurationElement": "Invalid xml. Expected element named 'configuration'.", 4 | "loc.messages.Error_NoMatchingFilesFoundForPattern": "No matching files were found with search pattern: %s", 5 | "loc.messages.Error_NoUrlWasFoundWhichMatches": "No download URL was found for %s", 6 | "loc.messages.Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 7 | "loc.messages.Error_NuGetToolInstallerFailer": "Tool install failed: %s", 8 | "loc.messages.Info_AvailableVersions": "The available versions are: %s", 9 | "loc.messages.Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", 10 | "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", 11 | "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 12 | "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", 13 | "loc.messages.Info_UsingToolPath": "Using tool path: %s", 14 | "loc.messages.Info_UsingVersion": "Using version: %s", 15 | "loc.messages.NGCommon_AddingSources": "Setting credentials in NuGet.config", 16 | "loc.messages.NGCommon_AreaNotFoundInSps": "Unable to locate the '%s' [%s] area. The service containing that area may not be available in your region.", 17 | "loc.messages.NGCommon_DetectedNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable): %s", 18 | "loc.messages.NGCommon_DetectedNuGetVersion": "Detected NuGet version %s / %s", 19 | "loc.messages.NGCommon_IgnoringNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable). Extensions are ignored when using the built-in NuGet client", 20 | "loc.messages.NGCommon_NoSourcesFoundInConfig": "No package sources were found in the NuGet.config file at %s", 21 | "loc.messages.NGCommon_NuGetConfigIsInvalid": "The NuGet.config at %s is invalid.", 22 | "loc.messages.NGCommon_NuGetConfigIsPackagesConfig": "Expected a NuGet.config file at %s, but its contents appear to be a packages.config. Check the settings for the %s task and confirm you selected NuGet.config rather than packages.config.", 23 | "loc.messages.NGCommon_RemovingSources": "Preparing to set credentials in NuGet.config", 24 | "loc.messages.NGCommon_SpsNotFound": "Unable to find the '%s' [%s] area. There may be a problem with your Team Foundation Server installation.", 25 | "loc.messages.NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.", 26 | "loc.messages.NGCommon_UnableToFindTool": "Unable to find tool %s", 27 | "loc.messages.Warning_SessionCreationFailed": "Could not create provenance session: %s", 28 | "loc.messages.Warning_UpdatingNuGetVersion": "Updating version of NuGet.exe to %s from %s. Behavior changes or breaking changes might occur as NuGet updates to a new version. If this is not desired, uncheck the 'Check for Latest Version' option in the task." 29 | } -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/Strings/resources.resjson/zh-TW/resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "loc.messages.Error_ApiKeyNotSupported": "DotNetCore currently does not support using an encrypted Api Key.", 3 | "loc.messages.Error_ExpectedConfigurationElement": "Invalid xml. Expected element named 'configuration'.", 4 | "loc.messages.Error_NoMatchingFilesFoundForPattern": "No matching files were found with search pattern: %s", 5 | "loc.messages.Error_NoUrlWasFoundWhichMatches": "No download URL was found for %s", 6 | "loc.messages.Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 7 | "loc.messages.Error_NuGetToolInstallerFailer": "Tool install failed: %s", 8 | "loc.messages.Info_AvailableVersions": "The available versions are: %s", 9 | "loc.messages.Info_ExpectBehaviorChangeWhenUsingVersionQuery": "You are using a query match on the version string. Behavior changes or breaking changes might occur as NuGet updates to a new version.", 10 | "loc.messages.Info_MatchingUrlWasFoundSettingAuth": "Using authentication information for the following URI: ", 11 | "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 12 | "loc.messages.Info_SavingTempConfig": "Saving NuGet.config to a temporary config file.", 13 | "loc.messages.Info_UsingToolPath": "Using tool path: %s", 14 | "loc.messages.Info_UsingVersion": "Using version: %s", 15 | "loc.messages.NGCommon_AddingSources": "Setting credentials in NuGet.config", 16 | "loc.messages.NGCommon_AreaNotFoundInSps": "Unable to locate the '%s' [%s] area. The service containing that area may not be available in your region.", 17 | "loc.messages.NGCommon_DetectedNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable): %s", 18 | "loc.messages.NGCommon_DetectedNuGetVersion": "Detected NuGet version %s / %s", 19 | "loc.messages.NGCommon_IgnoringNuGetExtensionsPath": "Detected NuGet extensions loader path (NUGET_EXTENSIONS_PATH environment variable). Extensions are ignored when using the built-in NuGet client", 20 | "loc.messages.NGCommon_NoSourcesFoundInConfig": "No package sources were found in the NuGet.config file at %s", 21 | "loc.messages.NGCommon_NuGetConfigIsInvalid": "The NuGet.config at %s is invalid.", 22 | "loc.messages.NGCommon_NuGetConfigIsPackagesConfig": "Expected a NuGet.config file at %s, but its contents appear to be a packages.config. Check the settings for the %s task and confirm you selected NuGet.config rather than packages.config.", 23 | "loc.messages.NGCommon_RemovingSources": "Preparing to set credentials in NuGet.config", 24 | "loc.messages.NGCommon_SpsNotFound": "Unable to find the '%s' [%s] area. There may be a problem with your Team Foundation Server installation.", 25 | "loc.messages.NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.", 26 | "loc.messages.NGCommon_UnableToFindTool": "Unable to find tool %s", 27 | "loc.messages.Warning_SessionCreationFailed": "Could not create provenance session: %s", 28 | "loc.messages.Warning_UpdatingNuGetVersion": "Updating version of NuGet.exe to %s from %s. Behavior changes or breaking changes might occur as NuGet updates to a new version. If this is not desired, uncheck the 'Check for Latest Version' option in the task." 29 | } -------------------------------------------------------------------------------- /vss-extension.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifestVersion": 1, 3 | "id": "PipelineArtifactCaching", 4 | "name": "Restores and saves pipeline artifacts with Universal Packages", 5 | "version": "1.0.0", 6 | "publisher": "1eslighthouseeng", 7 | "targets": [ 8 | { 9 | "id": "Microsoft.VisualStudio.Services" 10 | } 11 | ], 12 | "description": "Restores and saves pipeline artifacts from a cache given a key.", 13 | "categories": [ 14 | "Azure Pipelines" 15 | ], 16 | "files": [ 17 | { 18 | "path": "_build/Tasks/RestoreAndSaveCacheV1" 19 | }, 20 | { 21 | "path": "_build/Tasks/RestoreCacheV1" 22 | }, 23 | { 24 | "path": "_build/Tasks/SaveCacheV1" 25 | } 26 | ], 27 | "icons": { 28 | "default": "images/icon.png" 29 | }, 30 | "contributions": [ 31 | { 32 | "id": "RestoreAndSaveCacheV1", 33 | "type": "ms.vss-distributed-task.task", 34 | "targets": [ 35 | "ms.vss-distributed-task.tasks" 36 | ], 37 | "properties": { 38 | "name": "_build/Tasks/RestoreAndSaveCacheV1" 39 | } 40 | }, 41 | { 42 | "id": "RestoreCacheV1", 43 | "type": "ms.vss-distributed-task.task", 44 | "targets": [ 45 | "ms.vss-distributed-task.tasks" 46 | ], 47 | "properties": { 48 | "name": "_build/Tasks/RestoreCacheV1" 49 | } 50 | }, 51 | { 52 | "id": "SaveCacheV1", 53 | "type": "ms.vss-distributed-task.task", 54 | "targets": [ 55 | "ms.vss-distributed-task.tasks" 56 | ], 57 | "properties": { 58 | "name": "_build/Tasks/SaveCacheV1" 59 | } 60 | } 61 | ], 62 | "content": { 63 | "details": { 64 | "path": "README.md" 65 | }, 66 | "license": { 67 | "path": "LICENSE" 68 | } 69 | }, 70 | "links": { 71 | "repository": { 72 | "uri": "https://github.com/Microsoft/azure-pipelines-artifact-caching-tasks" 73 | }, 74 | "issues": { 75 | "uri": "https://github.com/Microsoft/azure-pipelines-artifact-caching-tasks/issues" 76 | }, 77 | "support": { 78 | "uri": "mailto:1es-lighthouse-eng@microsoft.com" 79 | } 80 | }, 81 | "repository": { 82 | "type": "git", 83 | "uri": "https://github.com/Microsoft/azure-pipelines-artifact-caching-tasks" 84 | }, 85 | "badges": [ 86 | { 87 | "href": "https://dev.azure.com/1es-cat/azure-pipelines-artifact-caching-tasks/_build/latest?definitionId=17&branchName=master", 88 | "uri": "https://dev.azure.com/1es-cat/azure-pipelines-artifact-caching-tasks/_apis/build/status/Microsoft.azure-pipelines-artifact-caching-tasks?branchName=master", 89 | "description": "Build" 90 | }, 91 | { 92 | "href": "https://dev.azure.com/1es-cat/azure-pipelines-artifact-caching-tasks/_release?definitionId=1", 93 | "uri": "https://vsrm.dev.azure.com/1es-cat/_apis/public/Release/badge/73af267c-80da-42c5-b634-ef63bb6d61fc/1/1", 94 | "description": "Release" 95 | } 96 | ] 97 | } -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/SaveCacheCacheMissDotTarget.ts: -------------------------------------------------------------------------------- 1 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 5 | import { UniversalMockHelper } from "packaging-common/Tests/UniversalMockHelper"; 6 | 7 | const taskPath = path.join(__dirname, "..", "savecache.js"); 8 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 9 | const hash = "caa22019107de89a8bbf9efd8a5273b37fa80bfb9c560e0bc349d5afd6df2ddc"; 10 | 11 | // provide answers for task mock 12 | const a: TaskLibAnswers = { 13 | findMatch: { 14 | ".build/commit": [".build/commit"], 15 | ".build": [".build", ".build/commit"], 16 | "**/out-build": ["src/out/out-build"] 17 | }, 18 | find: { 19 | DefaultWorkingDirectory: [ 20 | "src/out/out-build", 21 | ".build/commit", 22 | ".buildfoo/bar" 23 | ] 24 | }, 25 | rmRF: { 26 | "/users/home/DefaultWorkingDirectory/tmp_cache": { success: true }, 27 | "DefaultWorkingDirectory/tmp_cache": { success: true }, 28 | '"DefaultWorkingDirectory/tmp_cache"': { success: true } 29 | }, 30 | stats: { 31 | "src/out/out-build": { 32 | isDirectory() { 33 | return true; 34 | } 35 | }, 36 | ".build": { 37 | isDirectory: true 38 | }, 39 | ".build/commit": { 40 | isDirectory: false 41 | } 42 | }, 43 | exist: { 44 | "DefaultWorkingDirectory/tmp_cache": true 45 | }, 46 | checkPath: {}, 47 | exec: {}, 48 | which: {} 49 | }; 50 | 51 | tmr.setAnswers(a); 52 | 53 | const umh: UniversalMockHelper = new UniversalMockHelper( 54 | tmr, 55 | a, 56 | "/users/tmp/ArtifactTool.exe" 57 | ); 58 | 59 | umh.mockUniversalCommand( 60 | "publish", 61 | "node-package-feed", 62 | "builddefinition1", 63 | `1.0.0-${process.platform}-${hash}`, 64 | "DefaultWorkingDirectory/tmp_cache", 65 | { 66 | code: 0, 67 | stdout: "ArtifactTool.exe output", 68 | stderr: "" 69 | } 70 | ); 71 | 72 | tmr.setInput("keyFile", ".build/commit"); 73 | tmr.setInput("targetFolder", ".build"); 74 | 75 | const key = `${process.platform}-${hash}`.toUpperCase(); 76 | process.env[key] = "false"; 77 | process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = "DefaultWorkingDirectory"; 78 | 79 | // mock a specific module function called in task 80 | tmr.registerMock("fs", { 81 | readFileSync( 82 | path: string, 83 | options: 84 | | string 85 | | { 86 | encoding: string; 87 | flag?: string; 88 | } 89 | ): string { 90 | if (path.endsWith("/commit")) { 91 | const segments = path.split("/"); 92 | return segments.splice(segments.length - 2).join("/"); 93 | } 94 | return fs.readFileSync(path, options); 95 | }, 96 | chmodSync: fs.chmodSync, 97 | writeFileSync: fs.writeFileSync, 98 | readdirSync: fs.readdirSync, 99 | mkdirSync: fs.mkdirSync, 100 | copyFileSync: fs.copyFileSync, 101 | statSync: fs.statSync, 102 | linkSync: fs.linkSync, 103 | symlinkSync: fs.symlinkSync 104 | }); 105 | 106 | tmr.registerMock("shelljs", { 107 | exec(command: string) { 108 | console.log(`Mock exec: ${command}`); 109 | return { 110 | code: 0, 111 | stdout: "shelljs output", 112 | stderr: null 113 | }; 114 | } 115 | }); 116 | 117 | tmr.run(); 118 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/SaveCacheArtifactToolErr.ts: -------------------------------------------------------------------------------- 1 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 5 | import { UniversalMockHelper } from "packaging-common/Tests/UniversalMockHelper"; 6 | import { Constants } from "./Constants"; 7 | 8 | const taskPath = path.join(__dirname, "..", "savecache.js"); 9 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 10 | 11 | // provide answers for task mock 12 | const a: TaskLibAnswers = { 13 | findMatch: { 14 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 15 | "**/*/node_modules": [ 16 | "src/webapi/node_modules", 17 | "src/application/node_modules" 18 | ] 19 | }, 20 | find: { 21 | DefaultWorkingDirectory: [ 22 | "src/webapi/node_modules", 23 | "src/application/node_modules", 24 | "src/webapi/startup.config", 25 | "src/application/program.cs" 26 | ] 27 | }, 28 | rmRF: { 29 | "/users/home/DefaultWorkingDirectory/tmp_cache": { success: true }, 30 | "DefaultWorkingDirectory/tmp_cache": { success: true }, 31 | '"DefaultWorkingDirectory/tmp_cache"': { success: true } 32 | }, 33 | stats: { 34 | "src/webapi/node_modules": { 35 | isDirectory() { 36 | return true; 37 | } 38 | }, 39 | "src/application/node_modules": { 40 | isDirectory() { 41 | return true; 42 | } 43 | } 44 | }, 45 | exist: { 46 | "DefaultWorkingDirectory/tmp_cache": true 47 | }, 48 | checkPath: {}, 49 | exec: {}, 50 | which: {} 51 | }; 52 | 53 | tmr.setAnswers(a); 54 | 55 | const umh: UniversalMockHelper = new UniversalMockHelper(tmr, a, null); 56 | 57 | umh.mockUniversalCommand( 58 | "publish", 59 | "node-package-feed", 60 | "builddefinition1", 61 | `1.0.0-${process.platform}-${Constants.Hash}`, 62 | "DefaultWorkingDirectory/tmp_cache", 63 | { 64 | code: 0, 65 | stdout: "ArtifactTool.exe output", 66 | stderr: "" 67 | } 68 | ); 69 | 70 | tmr.setInput("keyFile", "**/*/yarn.lock"); 71 | tmr.setInput("targetFolder", "**/*/node_modules"); 72 | 73 | const key = `${process.platform}-${Constants.Hash}`.toUpperCase(); 74 | process.env[key] = "false"; 75 | process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = "DefaultWorkingDirectory"; 76 | 77 | // mock a specific module function called in task 78 | tmr.registerMock("fs", { 79 | readFileSync( 80 | path: string, 81 | options: 82 | | string 83 | | { 84 | encoding: string; 85 | flag?: string; 86 | } 87 | ): string { 88 | if (path.endsWith("/yarn.lock")) { 89 | const segments = path.split("/"); 90 | return segments.splice(segments.length - 3).join("/"); 91 | } 92 | return fs.readFileSync(path, options); 93 | }, 94 | chmodSync: fs.chmodSync, 95 | writeFileSync: fs.writeFileSync, 96 | readdirSync: fs.readdirSync, 97 | mkdirSync: fs.mkdirSync, 98 | copyFileSync: fs.copyFileSync, 99 | statSync: fs.statSync, 100 | linkSync: fs.linkSync, 101 | symlinkSync: fs.symlinkSync 102 | }); 103 | 104 | tmr.registerMock("shelljs", { 105 | exec(command: string) { 106 | console.log(`Mock exec: ${command}`); 107 | return { 108 | code: 0, 109 | stdout: "shelljs output", 110 | stderr: null 111 | }; 112 | } 113 | }); 114 | 115 | tmr.run(); 116 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/SaveCacheCacheMiss.ts: -------------------------------------------------------------------------------- 1 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 5 | import { UniversalMockHelper } from "packaging-common/Tests/UniversalMockHelper"; 6 | import { Constants } from "./Constants"; 7 | 8 | const taskPath = path.join(__dirname, "..", "savecache.js"); 9 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 10 | 11 | // provide answers for task mock 12 | const a: TaskLibAnswers = { 13 | findMatch: { 14 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 15 | "**/*/node_modules": [ 16 | "src/webapi/node_modules", 17 | "src/application/node_modules" 18 | ] 19 | }, 20 | find: { 21 | DefaultWorkingDirectory: [ 22 | "src/webapi/node_modules", 23 | "src/application/node_modules", 24 | "src/webapi/startup.config", 25 | "src/application/program.cs" 26 | ] 27 | }, 28 | rmRF: { 29 | "/users/home/DefaultWorkingDirectory/tmp_cache": { success: true }, 30 | "DefaultWorkingDirectory/tmp_cache": { success: true }, 31 | '"DefaultWorkingDirectory/tmp_cache"': { success: true } 32 | }, 33 | stats: { 34 | "src/webapi/node_modules": { 35 | isDirectory() { 36 | return true; 37 | } 38 | }, 39 | "src/application/node_modules": { 40 | isDirectory() { 41 | return true; 42 | } 43 | } 44 | }, 45 | exist: { 46 | "DefaultWorkingDirectory/tmp_cache": true 47 | }, 48 | checkPath: {}, 49 | exec: {}, 50 | which: {} 51 | }; 52 | 53 | tmr.setAnswers(a); 54 | 55 | const umh: UniversalMockHelper = new UniversalMockHelper( 56 | tmr, 57 | a, 58 | "/users/tmp/ArtifactTool.exe" 59 | ); 60 | 61 | umh.mockUniversalCommand( 62 | "publish", 63 | "node-package-feed", 64 | "builddefinition1", 65 | `1.0.0-${process.platform}-${Constants.Hash}`, 66 | "DefaultWorkingDirectory/tmp_cache", 67 | { 68 | code: 0, 69 | stdout: "ArtifactTool.exe output", 70 | stderr: "" 71 | } 72 | ); 73 | 74 | tmr.setInput("keyFile", "**/*/yarn.lock"); 75 | tmr.setInput("targetFolder", "**/*/node_modules"); 76 | 77 | const key = `${process.platform}-${Constants.Hash}`.toUpperCase(); 78 | process.env[key] = "false"; 79 | process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = "DefaultWorkingDirectory"; 80 | 81 | // mock a specific module function called in task 82 | tmr.registerMock("fs", { 83 | readFileSync( 84 | path: string, 85 | options: 86 | | string 87 | | { 88 | encoding: string; 89 | flag?: string; 90 | } 91 | ): string { 92 | if (path.endsWith("/yarn.lock")) { 93 | const segments = path.split("/"); 94 | return segments.splice(segments.length - 3).join("/"); 95 | } 96 | return fs.readFileSync(path, options); 97 | }, 98 | chmodSync: fs.chmodSync, 99 | writeFileSync: fs.writeFileSync, 100 | readdirSync: fs.readdirSync, 101 | mkdirSync: fs.mkdirSync, 102 | copyFileSync: fs.copyFileSync, 103 | statSync: fs.statSync, 104 | linkSync: fs.linkSync, 105 | symlinkSync: fs.symlinkSync 106 | }); 107 | 108 | tmr.registerMock("shelljs", { 109 | exec(command: string) { 110 | console.log(`Mock exec: ${command}`); 111 | return { 112 | code: 0, 113 | stdout: "shelljs output", 114 | stderr: null 115 | }; 116 | } 117 | }); 118 | 119 | tmr.run(); 120 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Tests/SaveCachePermissionsError.ts: -------------------------------------------------------------------------------- 1 | import * as tmrm from "azure-pipelines-task-lib/mock-run"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import { TaskLibAnswers } from "azure-pipelines-task-lib/mock-answer"; 5 | import { UniversalMockHelper } from "packaging-common/Tests/UniversalMockHelper"; 6 | import { Constants } from "./Constants"; 7 | 8 | const taskPath = path.join(__dirname, "..", "savecache.js"); 9 | const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); 10 | 11 | // provide answers for task mock 12 | const a: TaskLibAnswers = { 13 | findMatch: { 14 | "**/*/yarn.lock": ["src/webapi/yarn.lock", "src/application/yarn.lock"], 15 | "**/*/node_modules": [ 16 | "src/webapi/node_modules", 17 | "src/application/node_modules" 18 | ] 19 | }, 20 | find: { 21 | DefaultWorkingDirectory: [ 22 | "src/webapi/node_modules", 23 | "src/application/node_modules", 24 | "src/webapi/startup.config", 25 | "src/application/program.cs" 26 | ] 27 | }, 28 | rmRF: { 29 | "/users/home/DefaultWorkingDirectory/tmp_cache": { success: true }, 30 | "DefaultWorkingDirectory/tmp_cache": { success: true }, 31 | '"DefaultWorkingDirectory/tmp_cache"': { success: true } 32 | }, 33 | stats: { 34 | "src/webapi/node_modules": { 35 | isDirectory() { 36 | return true; 37 | } 38 | }, 39 | "src/application/node_modules": { 40 | isDirectory() { 41 | return true; 42 | } 43 | } 44 | }, 45 | exist: { 46 | "DefaultWorkingDirectory/tmp_cache": true 47 | }, 48 | checkPath: {}, 49 | exec: {}, 50 | which: {} 51 | }; 52 | 53 | tmr.setAnswers(a); 54 | 55 | const umh: UniversalMockHelper = new UniversalMockHelper( 56 | tmr, 57 | a, 58 | "/users/tmp/ArtifactTool.exe" 59 | ); 60 | 61 | umh.mockUniversalCommand( 62 | "publish", 63 | "node-package-feed", 64 | "builddefinition1", 65 | `1.0.0-${process.platform}-${Constants.Hash}`, 66 | "DefaultWorkingDirectory/tmp_cache", 67 | { 68 | code: 1, 69 | stdout: "ArtifactTool.exe output", 70 | stderr: 71 | "An error occurred on the service. User lacks permission to complete this action." 72 | } 73 | ); 74 | 75 | tmr.setInput("keyFile", "**/*/yarn.lock"); 76 | tmr.setInput("targetFolder", "**/*/node_modules"); 77 | 78 | const key = `${process.platform}-${Constants.Hash}`.toUpperCase(); 79 | process.env[key] = "false"; 80 | process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = "DefaultWorkingDirectory"; 81 | 82 | // mock a specific module function called in task 83 | tmr.registerMock("fs", { 84 | readFileSync( 85 | path: string, 86 | options: 87 | | string 88 | | { 89 | encoding: string; 90 | flag?: string; 91 | } 92 | ): string { 93 | if (path.endsWith("/yarn.lock")) { 94 | const segments = path.split("/"); 95 | return segments.splice(segments.length - 3).join("/"); 96 | } 97 | return fs.readFileSync(path, options); 98 | }, 99 | chmodSync: fs.chmodSync, 100 | writeFileSync: fs.writeFileSync, 101 | readdirSync: fs.readdirSync, 102 | mkdirSync: fs.mkdirSync, 103 | copyFileSync: fs.copyFileSync, 104 | statSync: fs.statSync, 105 | linkSync: fs.linkSync, 106 | symlinkSync: fs.symlinkSync 107 | }); 108 | 109 | tmr.registerMock("shelljs", { 110 | exec(command: string) { 111 | console.log(`Mock executing command: ${command}`); 112 | return { 113 | code: 0, 114 | stdout: "shelljs output", 115 | stderr: null 116 | }; 117 | } 118 | }); 119 | 120 | tmr.run(); 121 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | _temp/ 3 | # .vscode 4 | .DS_Store 5 | gulp-tsc-tmp-* 6 | .gulp-tsc-tmp-* 7 | 8 | tasks/**/*.js 9 | !**/mock_node_modules/**/*.js 10 | .taskkey 11 | 12 | ## Ignore Visual Studio temporary files, build results, and 13 | ## files generated by popular Visual Studio add-ons. 14 | 15 | # User-specific files 16 | *.suo 17 | *.user 18 | *.sln.docstates 19 | .vs/ 20 | 21 | # Build results 22 | [Dd]ebug/ 23 | [Dd]ebugPublic/ 24 | [Rr]elease/ 25 | x64/ 26 | !**/data/**/build/**/*.* 27 | bld/ 28 | [Bb]in/ 29 | [Oo]bj/ 30 | temp/ 31 | _common/ 32 | _build/ 33 | _download/ 34 | _package/ 35 | _test/ 36 | _working/ 37 | _gendocs/ 38 | 39 | # Roslyn cache directories 40 | *.ide/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | #NUNIT 47 | *.VisualState.xml 48 | TestResult.xml 49 | 50 | # Build Results of an ATL Project 51 | [Dd]ebugPS/ 52 | [Rr]eleasePS/ 53 | dlldata.c 54 | 55 | *_i.c 56 | *_p.c 57 | *_i.h 58 | *.ilk 59 | *.meta 60 | *.obj 61 | *.pch 62 | *.pdb 63 | *.pgc 64 | *.pgd 65 | *.rsp 66 | *.sbr 67 | *.tlb 68 | *.tli 69 | *.tlh 70 | *.tmp 71 | *.tmp_proj 72 | *.log 73 | *.vspscc 74 | *.vssscc 75 | .builds 76 | *.pidb 77 | *.svclog 78 | *.scc 79 | 80 | # Chutzpah Test files 81 | _Chutzpah* 82 | 83 | # Visual C++ cache files 84 | ipch/ 85 | *.aps 86 | *.ncb 87 | *.opensdf 88 | *.sdf 89 | *.cachefile 90 | 91 | # Visual Studio profiler 92 | *.psess 93 | *.vsp 94 | *.vspx 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding addin-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | 120 | # MightyMoose 121 | *.mm.* 122 | AutoTest.Net/ 123 | 124 | # Web workbench (sass) 125 | .sass-cache/ 126 | 127 | # Installshield output folder 128 | [Ee]xpress/ 129 | 130 | # DocProject is a documentation generator add-in 131 | DocProject/buildhelp/ 132 | DocProject/Help/*.HxT 133 | DocProject/Help/*.HxC 134 | DocProject/Help/*.hhc 135 | DocProject/Help/*.hhk 136 | DocProject/Help/*.hhp 137 | DocProject/Help/Html2 138 | DocProject/Help/html 139 | 140 | # Click-Once directory 141 | publish/ 142 | 143 | # Publish Web Output 144 | *.[Pp]ublish.xml 145 | *.azurePubxml 146 | ## TODO: Comment the next line if you want to checkin your 147 | ## web deploy settings but do note that will include unencrypted 148 | ## passwords 149 | #*.pubxml 150 | 151 | # NuGet Packages Directory 152 | packages/* 153 | ## TODO: If the tool you use requires repositories.config 154 | ## uncomment the next line 155 | #!packages/repositories.config 156 | 157 | # Enable "build/" folder in the NuGet Packages folder since 158 | # NuGet packages use it for MSBuild targets. 159 | # This line needs to be after the ignore of the build folder 160 | # (and the packages folder if the line above has been uncommented) 161 | !packages/build/ 162 | 163 | # Windows Azure Build Output 164 | csx/ 165 | *.build.csdef 166 | 167 | # Windows Store app package directory 168 | AppPackages/ 169 | 170 | # Others 171 | # sql/ 172 | *.Cache 173 | ClientBin/ 174 | [Ss]tyle[Cc]op.* 175 | ~$* 176 | *~ 177 | *.dbmdl 178 | *.dbproj.schemaview 179 | *.pfx 180 | *.publishsettings 181 | 182 | # RIA/Silverlight projects 183 | Generated_Code/ 184 | 185 | # Backup & report files from converting an old project file 186 | # to a newer Visual Studio version. Backup files are not needed, 187 | # because we have git ;-) 188 | _UpgradeReport_Files/ 189 | Backup*/ 190 | UpgradeLog*.XML 191 | UpgradeLog*.htm 192 | 193 | # SQL Server files 194 | *.mdf 195 | *.ldf 196 | 197 | # Business Intelligence projects 198 | *.rdl.data 199 | *.bim.layout 200 | *.bim_*.settings 201 | 202 | # Microsoft Fakes 203 | FakesAssemblies/ 204 | 205 | # LightSwitch generated files 206 | GeneratedArtifacts/ 207 | _Pvt_Extensions/ 208 | ModelManifest.xml 209 | /Package 210 | LoadTest.code-workspace 211 | *.vsix 212 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/cache/universaldownload.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as tl from "azure-pipelines-task-lib"; 5 | import * as pkgLocationUtils from "../locationUtilities"; 6 | import { IExecSyncResult, IExecOptions } from "azure-pipelines-task-lib/toolrunner"; 7 | import * as artifactToolRunner from "../ArtifactToolRunner"; 8 | import * as artifactToolUtilities from "../ArtifactToolUtilities"; 9 | import * as auth from "./Authentication"; 10 | import { UniversalPackagesResult } from "./universalPackages"; 11 | 12 | export async function run(artifactToolPath: string, hash: string, targetFolder: string): Promise { 13 | try { 14 | // Get directory to publish 15 | const downloadDir: string = targetFolder; 16 | if (downloadDir.length < 1) { 17 | tl.warning(tl.loc("Info_DownloadDirectoryNotFound")); 18 | return; 19 | } 20 | 21 | let serviceUri: string; 22 | let feedId: string; 23 | let packageName: string; 24 | let version: string; 25 | 26 | // Feed Auth 27 | let internalAuthInfo: auth.InternalAuthInfo; 28 | 29 | const toolRunnerOptions = artifactToolRunner.getOptions(); 30 | 31 | // getting inputs 32 | serviceUri = tl.getEndpointUrl("SYSTEMVSSCONNECTION", false); 33 | 34 | feedId = tl.getInput("feedList"); 35 | 36 | // Getting package name from hash 37 | const packageId = tl.getVariable('Build.DefinitionName') 38 | .replace(/\s/g, "") 39 | .substring(0, 255) 40 | .toLowerCase(); 41 | 42 | const accessToken = pkgLocationUtils.getSystemAccessToken(); 43 | 44 | internalAuthInfo = new auth.InternalAuthInfo([], accessToken); 45 | 46 | const feedUri = await pkgLocationUtils.getFeedUriFromBaseServiceUri(serviceUri, accessToken); 47 | packageName = await artifactToolUtilities.getPackageNameFromId(feedUri, accessToken, feedId, packageId); 48 | 49 | // Getting package version from hash 50 | version = `1.0.0-${hash}`; 51 | 52 | toolRunnerOptions.env.UNIVERSAL_DOWNLOAD_PAT = internalAuthInfo.accessToken; 53 | 54 | tl.debug(tl.loc("Info_UsingArtifactToolDownload")); 55 | 56 | const downloadOptions = { 57 | artifactToolPath, 58 | feedId, 59 | accountUrl: serviceUri, 60 | packageName, 61 | packageVersion: version, 62 | } as artifactToolRunner.IArtifactToolOptions; 63 | 64 | downloadPackageUsingArtifactTool(downloadDir, downloadOptions, toolRunnerOptions); 65 | 66 | console.log('artifact downloaded'); 67 | return { 68 | toolRan: true, 69 | success: true, 70 | }; 71 | } catch (err) { 72 | if (!err.message.includes("Can't find the package")) { 73 | tl.warning(err); 74 | return { 75 | toolRan: false, 76 | success: false, 77 | }; 78 | } 79 | 80 | return { 81 | toolRan: true, 82 | success: false, 83 | }; 84 | } 85 | } 86 | 87 | function downloadPackageUsingArtifactTool(downloadDir: string, options: artifactToolRunner.IArtifactToolOptions, execOptions: IExecOptions) { 88 | 89 | const command = new Array(); 90 | 91 | command.push("universal", "download", 92 | "--feed", options.feedId, 93 | "--service", options.accountUrl, 94 | "--package-name", options.packageName, 95 | "--package-version", options.packageVersion, 96 | "--path", downloadDir, 97 | "--patvar", "UNIVERSAL_DOWNLOAD_PAT", 98 | "--verbosity", tl.getInput("verbosity")); 99 | 100 | console.log(tl.loc("Info_Downloading", options.packageName, options.packageVersion, options.feedId)); 101 | const execResult: IExecSyncResult = artifactToolRunner.runArtifactTool(options.artifactToolPath, command, execOptions); 102 | 103 | if (execResult.code === 0) { 104 | return; 105 | } 106 | 107 | throw new Error(tl.loc("Error_UnexpectedErrorArtifactToolDownload", 108 | execResult.code, 109 | execResult.stderr ? execResult.stderr.trim() : execResult.stderr)); 110 | } 111 | -------------------------------------------------------------------------------- /Tasks/SaveCacheV1/Strings/resources.resjson/en-US/resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "loc.friendlyName": "Save cache", 3 | "loc.helpMarkDown": "", 4 | "loc.description": "Saves a cache with Universal Artifacts given a specified key.", 5 | "loc.instanceNameFormat": "Save artifact based on: $(keyfile)", 6 | "loc.input.label.keyfile": "Key file", 7 | "loc.input.help.keyfile": "The filename or wildcard used as the key to lookup the cache folder. For example, for npm based projects this would be '**/package-lock.json'.", 8 | "loc.input.label.targetfolder": "Target folder", 9 | "loc.input.help.targetfolder": "The folder/file or wildcard of items to cache. For example, node projects can cache packages with '**/node_modules, !**/node_modules/**/node_modules'.", 10 | "loc.input.label.feedList": "Feed", 11 | "loc.input.label.platformIndependent": "Platform Independent?", 12 | "loc.input.label.alias": "Cache alias", 13 | "loc.input.label.verbosity": "Verbosity", 14 | "loc.input.help.verbosity": "Specifies the amount of detail displayed in the output.", 15 | "loc.messages.PackagesDownloadedSuccessfully": "Package were downloaded successfully", 16 | "loc.messages.PackagesFailedToDownload": "Packages failed to download", 17 | "loc.messages.ConnectingAs": "Connecting to feeds in your Azure Pipelines/TFS project collection as '%s' [%s]", 18 | "loc.messages.BuildIdentityPermissionsHint": "For internal feeds, make sure the build service identity '%s' [%s] has access to the feed.", 19 | "loc.messages.CouldNotFindUniversalPackagesService": "Could not find the Universal Packages service. This task will not be able to authenticate to feeds hosted in your Azure Pipelines/TFS project collection.", 20 | "loc.messages.Error_NoValueFoundForEnvVar": "No value was found for the provided environment variable.", 21 | "loc.messages.PackagesPublishedSuccessfully": "Packages were published successfully", 22 | "loc.messages.PackagesFailedToPublish": "Packages failed to publish", 23 | "loc.messages.UnknownFeedType": "Unknown feed type '%s'", 24 | "loc.messages.Error_NoSourceSpecifiedForPublish": "No external source was specified for publish", 25 | "loc.messages.Error_NoSourceSpecifiedForDownload": "No external source was specified for download", 26 | "loc.messages.Error_UnexpectedErrorArtifactTool": "An unexpected error occurred while trying to push the package. Exit code(%s) and error(%s)", 27 | "loc.messages.Error_UnexpectedErrorArtifactToolDownload": "An unexpected error occurred while trying to download the package. Exit code(%s) and error(%s)", 28 | "loc.messages.Info_DownloadDirectoryNotFound": "Download directory not found or it did not matched the search pattern.", 29 | "loc.messages.Info_PublishDirectoryNotFound": "Publish directory not found or it did not matched the search pattern.", 30 | "loc.messages.Error_CommandNotRecognized": "The command %s was not recognized.", 31 | "loc.messages.Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 32 | "loc.messages.Error_AuthNotSupported": "Authentication using username/password not supported for Universal Packages. Please use personal access tokens.", 33 | "loc.messages.Info_DownloadingArtifactTool": "Downloading artifact tool from %s", 34 | "loc.messages.Info_Downloading": "Downloading package: %s, version: %s using feed id: %s", 35 | "loc.messages.Info_Publishing": "Publishing package: %s, version: %s using feed id: %s", 36 | "loc.messages.Info_UsingArtifactToolPublish": "Using artifact tool to publish the package", 37 | "loc.messages.Info_UsingArtifactToolDownload": "Using artifact tool to download the package", 38 | "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 39 | "loc.messages.Info_UsingToolPath": "Using tool path: %s", 40 | "loc.messages.Info_UsingVersion": "Using version: %s", 41 | "loc.messages.FailedToGetPackageMetadata": "Failed to get package metadata.", 42 | "loc.messages.FailedToGetArtifactTool": "Failed to get artifact tool from service. %s", 43 | "loc.messages.Error_UnexpectedErrorFailedToGetToolMetadata": "Failed to get artifact tool metadata from source url %s", 44 | "loc.messages.FailedToGetLatestPackageVersion": "Failed to get package versions", 45 | "loc.messages.Warn_CredentialsNotFound": "Could not determine credentials to use for Universal Packages", 46 | "loc.messages.Error_UniversalPackagesNotSupportedOnPrem": "Universal Packages are not supported in Azure DevOps Server." 47 | } -------------------------------------------------------------------------------- /Tasks/RestoreCacheV1/Strings/resources.resjson/en-US/resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "loc.friendlyName": "Restore cache", 3 | "loc.helpMarkDown": "", 4 | "loc.description": "Restore a folder from a cache given a specified key.", 5 | "loc.instanceNameFormat": "Restore artifact based on: $(keyfile)", 6 | "loc.input.label.keyfile": "Key file", 7 | "loc.input.help.keyfile": "The filename or wildcard used as the key to lookup the cache folder. For example, for npm based projects this would be '**/package-lock.json'.", 8 | "loc.input.label.targetfolder": "Target folder", 9 | "loc.input.help.targetfolder": "The folder/file or wildcard of items to cache. For example, node projects can cache packages with '**/node_modules, !**/node_modules/**/node_modules'.", 10 | "loc.input.label.feedList": "Feed", 11 | "loc.input.label.platformIndependent": "Platform Independent?", 12 | "loc.input.label.dryRun": "Dry run", 13 | "loc.input.label.alias": "Cache alias", 14 | "loc.input.label.verbosity": "Verbosity", 15 | "loc.input.help.verbosity": "Specifies the amount of detail displayed in the output.", 16 | "loc.messages.PackagesDownloadedSuccessfully": "Package were downloaded successfully", 17 | "loc.messages.PackagesFailedToDownload": "Packages failed to download", 18 | "loc.messages.ConnectingAs": "Connecting to feeds in your Azure Pipelines/TFS project collection as '%s' [%s]", 19 | "loc.messages.BuildIdentityPermissionsHint": "For internal feeds, make sure the build service identity '%s' [%s] has access to the feed.", 20 | "loc.messages.CouldNotFindUniversalPackagesService": "Could not find the Universal Packages service. This task will not be able to authenticate to feeds hosted in your Azure Pipelines/TFS project collection.", 21 | "loc.messages.Error_NoValueFoundForEnvVar": "No value was found for the provided environment variable.", 22 | "loc.messages.PackagesPublishedSuccessfully": "Packages were published successfully", 23 | "loc.messages.PackagesFailedToPublish": "Packages failed to publish", 24 | "loc.messages.UnknownFeedType": "Unknown feed type '%s'", 25 | "loc.messages.Error_NoSourceSpecifiedForPublish": "No external source was specified for publish", 26 | "loc.messages.Error_NoSourceSpecifiedForDownload": "No external source was specified for download", 27 | "loc.messages.Error_UnexpectedErrorArtifactTool": "An unexpected error occurred while trying to push the package. Exit code(%s) and error(%s)", 28 | "loc.messages.Error_UnexpectedErrorArtifactToolDownload": "An unexpected error occurred while trying to download the package. Exit code(%s) and error(%s)", 29 | "loc.messages.Info_DownloadDirectoryNotFound": "Download directory not found or it did not matched the search pattern.", 30 | "loc.messages.Info_PublishDirectoryNotFound": "Publish directory not found or it did not matched the search pattern.", 31 | "loc.messages.Error_CommandNotRecognized": "The command %s was not recognized.", 32 | "loc.messages.Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 33 | "loc.messages.Error_AuthNotSupported": "Authentication using username/password not supported for Universal Packages. Please use personal access tokens.", 34 | "loc.messages.Info_DownloadingArtifactTool": "Downloading artifact tool from %s", 35 | "loc.messages.Info_Downloading": "Downloading package: %s, version: %s using feed id: %s", 36 | "loc.messages.Info_Publishing": "Publishing package: %s, version: %s using feed id: %s", 37 | "loc.messages.Info_UsingArtifactToolPublish": "Using artifact tool to publish the package", 38 | "loc.messages.Info_UsingArtifactToolDownload": "Using artifact tool to download the package", 39 | "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 40 | "loc.messages.Info_UsingToolPath": "Using tool path: %s", 41 | "loc.messages.Info_UsingVersion": "Using version: %s", 42 | "loc.messages.FailedToGetPackageMetadata": "Failed to get package metadata.", 43 | "loc.messages.FailedToGetArtifactTool": "Failed to get artifact tool from service. %s", 44 | "loc.messages.Error_UnexpectedErrorFailedToGetToolMetadata": "Failed to get artifact tool metadata from source url %s", 45 | "loc.messages.FailedToGetLatestPackageVersion": "Failed to get package versions", 46 | "loc.messages.Warn_CredentialsNotFound": "Could not determine credentials to use for Universal Packages", 47 | "loc.messages.Error_UniversalPackagesNotSupportedOnPrem": "Universal Packages are not supported in Azure DevOps Server." 48 | } -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/Strings/resources.resjson/en-US/resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "loc.friendlyName": "Restore and save cache", 3 | "loc.helpMarkDown": "", 4 | "loc.description": "Restores and saves a folder given a specified key.", 5 | "loc.instanceNameFormat": "Restore and save artifact based on: $(keyfile)", 6 | "loc.input.label.keyfile": "Key file", 7 | "loc.input.help.keyfile": "The filename or wildcard used as the key to lookup the cache folder. For example, for npm based projects this would be '**/package-lock.json'.", 8 | "loc.input.label.targetfolder": "Target folder", 9 | "loc.input.help.targetfolder": "The folder/file or wildcard of items to cache. For example, node projects can cache packages with '**/node_modules, !**/node_modules/**/node_modules'.", 10 | "loc.input.label.feedList": "Feed", 11 | "loc.input.label.platformIndependent": "Platform Independent?", 12 | "loc.input.label.dryRun": "Dry run", 13 | "loc.input.label.alias": "Cache alias", 14 | "loc.input.label.verbosity": "Verbosity", 15 | "loc.input.help.verbosity": "Specifies the amount of detail displayed in the output.", 16 | "loc.messages.PackagesDownloadedSuccessfully": "Package were downloaded successfully", 17 | "loc.messages.PackagesFailedToDownload": "Packages failed to download", 18 | "loc.messages.ConnectingAs": "Connecting to feeds in your Azure Pipelines/TFS project collection as '%s' [%s]", 19 | "loc.messages.BuildIdentityPermissionsHint": "For internal feeds, make sure the build service identity '%s' [%s] has access to the feed.", 20 | "loc.messages.CouldNotFindUniversalPackagesService": "Could not find the Universal Packages service. This task will not be able to authenticate to feeds hosted in your Azure Pipelines/TFS project collection.", 21 | "loc.messages.Error_NoValueFoundForEnvVar": "No value was found for the provided environment variable.", 22 | "loc.messages.PackagesPublishedSuccessfully": "Packages were published successfully", 23 | "loc.messages.PackagesFailedToPublish": "Packages failed to publish", 24 | "loc.messages.UnknownFeedType": "Unknown feed type '%s'", 25 | "loc.messages.Error_NoSourceSpecifiedForPublish": "No external source was specified for publish", 26 | "loc.messages.Error_NoSourceSpecifiedForDownload": "No external source was specified for download", 27 | "loc.messages.Error_UnexpectedErrorArtifactTool": "An unexpected error occurred while trying to push the package. Exit code(%s) and error(%s)", 28 | "loc.messages.Error_UnexpectedErrorArtifactToolDownload": "An unexpected error occurred while trying to download the package. Exit code(%s) and error(%s)", 29 | "loc.messages.Info_DownloadDirectoryNotFound": "Download directory not found or it did not matched the search pattern.", 30 | "loc.messages.Info_PublishDirectoryNotFound": "Publish directory not found or it did not matched the search pattern.", 31 | "loc.messages.Error_CommandNotRecognized": "The command %s was not recognized.", 32 | "loc.messages.Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 33 | "loc.messages.Error_AuthNotSupported": "Authentication using username/password not supported for Universal Packages. Please use personal access tokens.", 34 | "loc.messages.Info_DownloadingArtifactTool": "Downloading artifact tool from %s", 35 | "loc.messages.Info_Downloading": "Downloading package: %s, version: %s using feed id: %s", 36 | "loc.messages.Info_Publishing": "Publishing package: %s, version: %s using feed id: %s", 37 | "loc.messages.Info_UsingArtifactToolPublish": "Using artifact tool to publish the package", 38 | "loc.messages.Info_UsingArtifactToolDownload": "Using artifact tool to download the package", 39 | "loc.messages.Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 40 | "loc.messages.Info_UsingToolPath": "Using tool path: %s", 41 | "loc.messages.Info_UsingVersion": "Using version: %s", 42 | "loc.messages.FailedToGetPackageMetadata": "Failed to get package metadata.", 43 | "loc.messages.FailedToGetArtifactTool": "Failed to get artifact tool from service. %s", 44 | "loc.messages.Error_UnexpectedErrorFailedToGetToolMetadata": "Failed to get artifact tool metadata from source url %s", 45 | "loc.messages.FailedToGetLatestPackageVersion": "Failed to get package versions", 46 | "loc.messages.Warn_CredentialsNotFound": "Could not determine credentials to use for Universal Packages", 47 | "loc.messages.Error_UniversalPackagesNotSupportedOnPrem": "Universal Packages are not supported in Azure DevOps Server." 48 | } -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/cache/universalpublish.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as pkgLocationUtils from "../locationUtilities"; 5 | import { ProvenanceHelper } from "../provenance"; 6 | import * as tl from "azure-pipelines-task-lib"; 7 | import { IExecOptions, IExecSyncResult } from "azure-pipelines-task-lib/toolrunner"; 8 | import * as artifactToolRunner from "../ArtifactToolRunner"; 9 | import * as artifactToolUtilities from "../ArtifactToolUtilities"; 10 | import * as auth from "./Authentication"; 11 | import { UniversalPackagesResult } from "./universalPackages"; 12 | 13 | export async function run(artifactToolPath: string, hash: string, targetFolder: string): Promise { 14 | const buildIdentityDisplayName: string = null; 15 | const buildIdentityAccount: string = null; 16 | try { 17 | // Get directory to publish 18 | const publishDir: string = targetFolder; 19 | let serviceUri: string; 20 | let feedId: string; 21 | let packageName: string; 22 | const version: string = `1.0.0-${hash}`; 23 | let accessToken: string; 24 | let feedUri: string; 25 | const publishedPackageVar: string = tl.getInput("publishedPackageVar"); 26 | const versionRadio = 'custom'; 27 | 28 | let internalAuthInfo: auth.InternalAuthInfo; 29 | 30 | const toolRunnerOptions = artifactToolRunner.getOptions(); 31 | 32 | let sessionId: string; 33 | 34 | // getting inputs 35 | serviceUri = tl.getEndpointUrl("SYSTEMVSSCONNECTION", false); 36 | 37 | packageName = tl.getVariable('Build.DefinitionName') 38 | .replace(/\s/g, "") 39 | .substring(0, 255) 40 | .toLowerCase(); 41 | 42 | feedId = tl.getInput("feedList"); 43 | 44 | // Setting up auth info 45 | accessToken = pkgLocationUtils.getSystemAccessToken(); 46 | internalAuthInfo = new auth.InternalAuthInfo([], accessToken); 47 | 48 | toolRunnerOptions.env.UNIVERSAL_PUBLISH_PAT = internalAuthInfo.accessToken; 49 | 50 | // creating session 51 | const useSessionEnabled = tl.getVariable("Packaging.SavePublishMetadata"); 52 | if (useSessionEnabled) { 53 | let packagingLocation: string; 54 | try { 55 | // This call is to get the packaging URI(abc.pkgs.vs.com) which is same for all protocols. 56 | packagingLocation = await pkgLocationUtils.getNuGetUriFromBaseServiceUri( 57 | serviceUri, 58 | accessToken); 59 | } catch (error) { 60 | tl.debug(JSON.stringify(error)); 61 | packagingLocation = serviceUri; 62 | } 63 | 64 | const pkgConn = pkgLocationUtils.getWebApiWithProxy(packagingLocation, accessToken); 65 | sessionId = await ProvenanceHelper.GetSessionId( 66 | feedId, 67 | "upack", /* must match protocol name on the server */ 68 | pkgConn.serverUrl, 69 | [pkgConn.authHandler], 70 | pkgConn.options); 71 | } 72 | 73 | tl.debug(tl.loc("Info_UsingArtifactToolPublish")); 74 | 75 | if (sessionId != null) { 76 | feedId = sessionId; 77 | } 78 | 79 | // tslint:disable-next-line:no-object-literal-type-assertion 80 | const publishOptions = { 81 | artifactToolPath, 82 | feedId, 83 | accountUrl: serviceUri, 84 | packageName, 85 | packageVersion: version, 86 | } as artifactToolRunner.IArtifactToolOptions; 87 | 88 | publishPackageUsingArtifactTool(publishDir, publishOptions, toolRunnerOptions); 89 | if (publishedPackageVar) { 90 | tl.setVariable(publishedPackageVar, `${packageName} ${version}`); 91 | } 92 | 93 | return { 94 | toolRan: true, 95 | success: true, 96 | }; 97 | } catch (err) { 98 | tl.warning(`Issue saving package: ${err}`); 99 | return { 100 | toolRan: true, 101 | success: false, 102 | }; 103 | } 104 | } 105 | 106 | function publishPackageUsingArtifactTool(publishDir: string, options: artifactToolRunner.IArtifactToolOptions, execOptions: IExecOptions) { 107 | const command = new Array(); 108 | command.push("universal", "publish", 109 | "--feed", options.feedId, 110 | "--service", options.accountUrl, 111 | "--package-name", options.packageName, 112 | "--package-version", options.packageVersion, 113 | "--path", publishDir, 114 | "--patvar", "UNIVERSAL_PUBLISH_PAT", 115 | "--verbosity", tl.getInput("verbosity")); 116 | 117 | console.log(tl.loc("Info_Publishing", options.packageName, options.packageVersion, options.feedId)); 118 | const execResult: IExecSyncResult = artifactToolRunner.runArtifactTool(options.artifactToolPath, command, execOptions); 119 | 120 | if (execResult.code === 0) { 121 | return; 122 | } 123 | 124 | throw new Error(tl.loc("Error_UnexpectedErrorArtifactTool", 125 | execResult.code, 126 | execResult.stderr ? execResult.stderr.trim() : execResult.stderr)); 127 | } 128 | -------------------------------------------------------------------------------- /Tasks/SaveCacheV1/task.loc.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "2474c51d-d93a-4815-9d4c-3b9f19b11cb0", 3 | "name": "SaveCache", 4 | "friendlyName": "ms-resource:loc.friendlyName", 5 | "description": "ms-resource:loc.description", 6 | "helpMarkDown": "ms-resource:loc.helpMarkDown", 7 | "category": "Utility", 8 | "author": "Microsoft Corp", 9 | "version": { 10 | "Major": 1, 11 | "Minor": 0, 12 | "Patch": 18 13 | }, 14 | "instanceNameFormat": "ms-resource:loc.instanceNameFormat", 15 | "inputs": [ 16 | { 17 | "name": "keyfile", 18 | "type": "filePath", 19 | "label": "ms-resource:loc.input.label.keyfile", 20 | "defaultValue": "", 21 | "required": true, 22 | "helpMarkDown": "ms-resource:loc.input.help.keyfile" 23 | }, 24 | { 25 | "name": "targetfolder", 26 | "type": "filePath", 27 | "label": "ms-resource:loc.input.label.targetfolder", 28 | "defaultValue": "", 29 | "required": true, 30 | "helpMarkDown": "ms-resource:loc.input.help.targetfolder" 31 | }, 32 | { 33 | "name": "feedList", 34 | "aliases": [ 35 | "vstsFeed" 36 | ], 37 | "type": "pickList", 38 | "label": "ms-resource:loc.input.label.feedList", 39 | "defaultValue": "", 40 | "required": "true" 41 | }, 42 | { 43 | "name": "platformIndependent", 44 | "type": "boolean", 45 | "label": "ms-resource:loc.input.label.platformIndependent", 46 | "description": "Whether the cached artifact is platform independent (default is false).", 47 | "defaultValue": false, 48 | "required": "false" 49 | }, 50 | { 51 | "name": "alias", 52 | "type": "string", 53 | "label": "ms-resource:loc.input.label.alias", 54 | "description": "An optional alias to the cache to control the name of the output variable (E.g. An alias of 'Build' sets the output variable 'CacheRestored-Build').", 55 | "defaultValue": "", 56 | "required": "false" 57 | }, 58 | { 59 | "name": "verbosity", 60 | "type": "pickList", 61 | "label": "ms-resource:loc.input.label.verbosity", 62 | "defaultValue": "None", 63 | "helpMarkDown": "ms-resource:loc.input.help.verbosity", 64 | "required": "false", 65 | "groupName": "advanced", 66 | "options": { 67 | "None": "None", 68 | "Trace": "Trace", 69 | "Debug": "Debug", 70 | "Information": "Information", 71 | "Warning": "Warning", 72 | "Error": "Error", 73 | "Critical": "Citical" 74 | } 75 | } 76 | ], 77 | "dataSourceBindings": [ 78 | { 79 | "target": "feedList", 80 | "endpointId": "tfs:feed", 81 | "endpointUrl": "{{endpoint.url}}/_apis/packaging/feeds", 82 | "resultSelector": "jsonpath:$.value[*]", 83 | "resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{name}}}\" }" 84 | } 85 | ], 86 | "execution": { 87 | "Node": { 88 | "target": "savecache.js" 89 | } 90 | }, 91 | "messages": { 92 | "PackagesDownloadedSuccessfully": "ms-resource:loc.messages.PackagesDownloadedSuccessfully", 93 | "PackagesFailedToDownload": "ms-resource:loc.messages.PackagesFailedToDownload", 94 | "ConnectingAs": "ms-resource:loc.messages.ConnectingAs", 95 | "BuildIdentityPermissionsHint": "ms-resource:loc.messages.BuildIdentityPermissionsHint", 96 | "CouldNotFindUniversalPackagesService": "ms-resource:loc.messages.CouldNotFindUniversalPackagesService", 97 | "Error_NoValueFoundForEnvVar": "ms-resource:loc.messages.Error_NoValueFoundForEnvVar", 98 | "PackagesPublishedSuccessfully": "ms-resource:loc.messages.PackagesPublishedSuccessfully", 99 | "PackagesFailedToPublish": "ms-resource:loc.messages.PackagesFailedToPublish", 100 | "UnknownFeedType": "ms-resource:loc.messages.UnknownFeedType", 101 | "Error_NoSourceSpecifiedForPublish": "ms-resource:loc.messages.Error_NoSourceSpecifiedForPublish", 102 | "Error_NoSourceSpecifiedForDownload": "ms-resource:loc.messages.Error_NoSourceSpecifiedForDownload", 103 | "Error_UnexpectedErrorArtifactTool": "ms-resource:loc.messages.Error_UnexpectedErrorArtifactTool", 104 | "Error_UnexpectedErrorArtifactToolDownload": "ms-resource:loc.messages.Error_UnexpectedErrorArtifactToolDownload", 105 | "Info_DownloadDirectoryNotFound": "ms-resource:loc.messages.Info_DownloadDirectoryNotFound", 106 | "Info_PublishDirectoryNotFound": "ms-resource:loc.messages.Info_PublishDirectoryNotFound", 107 | "Error_CommandNotRecognized": "ms-resource:loc.messages.Error_CommandNotRecognized", 108 | "Error_NoVersionWasFoundWhichMatches": "ms-resource:loc.messages.Error_NoVersionWasFoundWhichMatches", 109 | "Error_AuthNotSupported": "ms-resource:loc.messages.Error_AuthNotSupported", 110 | "Info_DownloadingArtifactTool": "ms-resource:loc.messages.Info_DownloadingArtifactTool", 111 | "Info_Downloading": "ms-resource:loc.messages.Info_Downloading", 112 | "Info_Publishing": "ms-resource:loc.messages.Info_Publishing", 113 | "Info_UsingArtifactToolPublish": "ms-resource:loc.messages.Info_UsingArtifactToolPublish", 114 | "Info_UsingArtifactToolDownload": "ms-resource:loc.messages.Info_UsingArtifactToolDownload", 115 | "Info_ResolvedToolFromCache": "ms-resource:loc.messages.Info_ResolvedToolFromCache", 116 | "Info_UsingToolPath": "ms-resource:loc.messages.Info_UsingToolPath", 117 | "Info_UsingVersion": "ms-resource:loc.messages.Info_UsingVersion", 118 | "FailedToGetPackageMetadata": "ms-resource:loc.messages.FailedToGetPackageMetadata", 119 | "FailedToGetArtifactTool": "ms-resource:loc.messages.FailedToGetArtifactTool", 120 | "Error_UnexpectedErrorFailedToGetToolMetadata": "ms-resource:loc.messages.Error_UnexpectedErrorFailedToGetToolMetadata", 121 | "FailedToGetLatestPackageVersion": "ms-resource:loc.messages.FailedToGetLatestPackageVersion", 122 | "Warn_CredentialsNotFound": "ms-resource:loc.messages.Warn_CredentialsNotFound", 123 | "Error_UniversalPackagesNotSupportedOnPrem": "ms-resource:loc.messages.Error_UniversalPackagesNotSupportedOnPrem" 124 | } 125 | } -------------------------------------------------------------------------------- /Tasks/SaveCacheV1/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "2474c51d-d93a-4815-9d4c-3b9f19b11cb0", 3 | "name": "SaveCache", 4 | "friendlyName": "Save cache", 5 | "description": "Saves a cache with Universal Artifacts given a specified key.", 6 | "helpMarkDown": "", 7 | "category": "Utility", 8 | "author": "Microsoft Corp", 9 | "version": { 10 | "Major": 1, 11 | "Minor": 0, 12 | "Patch": 18 13 | }, 14 | "instanceNameFormat": "Save artifact based on: $(keyfile)", 15 | "inputs": [ 16 | { 17 | "name": "keyfile", 18 | "type": "filePath", 19 | "label": "Key file", 20 | "defaultValue": "", 21 | "required": true, 22 | "helpMarkDown": "The filename or wildcard used as the key to lookup the cache folder. For example, for npm based projects this would be '**/package-lock.json'." 23 | }, 24 | { 25 | "name": "targetfolder", 26 | "type": "filePath", 27 | "label": "Target folder", 28 | "defaultValue": "", 29 | "required": true, 30 | "helpMarkDown": "The folder/file or wildcard of items to cache. For example, node projects can cache packages with '**/node_modules, !**/node_modules/**/node_modules'." 31 | }, 32 | { 33 | "name": "feedList", 34 | "aliases": ["vstsFeed"], 35 | "type": "pickList", 36 | "label": "Feed", 37 | "defaultValue": "", 38 | "required": "true" 39 | }, 40 | { 41 | "name": "platformIndependent", 42 | "type": "boolean", 43 | "label": "Platform Independent?", 44 | "description": "Whether the cached artifact is platform independent (default is false).", 45 | "defaultValue": false, 46 | "required": "false" 47 | }, 48 | { 49 | "name": "alias", 50 | "type": "string", 51 | "label": "Cache alias", 52 | "description": "An optional alias to the cache to control the name of the output variable (E.g. An alias of 'Build' sets the output variable 'CacheRestored-Build').", 53 | "defaultValue": "", 54 | "required": "false" 55 | }, 56 | { 57 | "name": "verbosity", 58 | "type": "pickList", 59 | "label": "Verbosity", 60 | "defaultValue": "None", 61 | "helpMarkDown": "Specifies the amount of detail displayed in the output.", 62 | "required": "false", 63 | "groupName": "advanced", 64 | "options": { 65 | "None": "None", 66 | "Trace": "Trace", 67 | "Debug": "Debug", 68 | "Information": "Information", 69 | "Warning": "Warning", 70 | "Error": "Error", 71 | "Critical": "Citical" 72 | } 73 | } 74 | ], 75 | "dataSourceBindings": [ 76 | { 77 | "target": "feedList", 78 | "endpointId": "tfs:feed", 79 | "endpointUrl": "{{endpoint.url}}/_apis/packaging/feeds", 80 | "resultSelector": "jsonpath:$.value[*]", 81 | "resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{name}}}\" }" 82 | } 83 | ], 84 | "execution": { 85 | "Node": { 86 | "target": "savecache.js" 87 | } 88 | }, 89 | "messages": { 90 | "PackagesDownloadedSuccessfully": "Package were downloaded successfully", 91 | "PackagesFailedToDownload": "Packages failed to download", 92 | "ConnectingAs": "Connecting to feeds in your Azure Pipelines/TFS project collection as '%s' [%s]", 93 | "BuildIdentityPermissionsHint": "For internal feeds, make sure the build service identity '%s' [%s] has access to the feed.", 94 | "CouldNotFindUniversalPackagesService": "Could not find the Universal Packages service. This task will not be able to authenticate to feeds hosted in your Azure Pipelines/TFS project collection.", 95 | "Error_NoValueFoundForEnvVar": "No value was found for the provided environment variable.", 96 | "PackagesPublishedSuccessfully": "Packages were published successfully", 97 | "PackagesFailedToPublish": "Packages failed to publish", 98 | "UnknownFeedType": "Unknown feed type '%s'", 99 | "Error_NoSourceSpecifiedForPublish": "No external source was specified for publish", 100 | "Error_NoSourceSpecifiedForDownload": "No external source was specified for download", 101 | "Error_UnexpectedErrorArtifactTool": "An unexpected error occurred while trying to push the package. Exit code(%s) and error(%s)", 102 | "Error_UnexpectedErrorArtifactToolDownload": "An unexpected error occurred while trying to download the package. Exit code(%s) and error(%s)", 103 | "Info_DownloadDirectoryNotFound": "Download directory not found or it did not matched the search pattern.", 104 | "Info_PublishDirectoryNotFound": "Publish directory not found or it did not matched the search pattern.", 105 | "Error_CommandNotRecognized": "The command %s was not recognized.", 106 | "Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 107 | "Error_AuthNotSupported": "Authentication using username/password not supported for Universal Packages. Please use personal access tokens.", 108 | "Info_DownloadingArtifactTool": "Downloading artifact tool from %s", 109 | "Info_Downloading": "Downloading package: %s, version: %s using feed id: %s", 110 | "Info_Publishing": "Publishing package: %s, version: %s using feed id: %s", 111 | "Info_UsingArtifactToolPublish": "Using artifact tool to publish the package", 112 | "Info_UsingArtifactToolDownload": "Using artifact tool to download the package", 113 | "Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 114 | "Info_UsingToolPath": "Using tool path: %s", 115 | "Info_UsingVersion": "Using version: %s", 116 | "FailedToGetPackageMetadata": "Failed to get package metadata.", 117 | "FailedToGetArtifactTool": "Failed to get artifact tool from service. %s", 118 | "Error_UnexpectedErrorFailedToGetToolMetadata": "Failed to get artifact tool metadata from source url %s", 119 | "FailedToGetLatestPackageVersion": "Failed to get package versions", 120 | "Warn_CredentialsNotFound": "Could not determine credentials to use for Universal Packages", 121 | "Error_UniversalPackagesNotSupportedOnPrem": "Universal Packages are not supported in Azure DevOps Server." 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Tasks/RestoreCacheV1/task.loc.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "9aea8869-034d-4094-a6ad-880767d0686c", 3 | "name": "RestoreCache", 4 | "friendlyName": "ms-resource:loc.friendlyName", 5 | "description": "ms-resource:loc.description", 6 | "helpMarkDown": "ms-resource:loc.helpMarkDown", 7 | "category": "Utility", 8 | "author": "Microsoft Corp", 9 | "version": { 10 | "Major": 1, 11 | "Minor": 0, 12 | "Patch": 18 13 | }, 14 | "instanceNameFormat": "ms-resource:loc.instanceNameFormat", 15 | "inputs": [ 16 | { 17 | "name": "keyfile", 18 | "type": "filePath", 19 | "label": "ms-resource:loc.input.label.keyfile", 20 | "defaultValue": "", 21 | "required": true, 22 | "helpMarkDown": "ms-resource:loc.input.help.keyfile" 23 | }, 24 | { 25 | "name": "targetfolder", 26 | "type": "filePath", 27 | "label": "ms-resource:loc.input.label.targetfolder", 28 | "defaultValue": "", 29 | "required": true, 30 | "helpMarkDown": "ms-resource:loc.input.help.targetfolder" 31 | }, 32 | { 33 | "name": "feedList", 34 | "aliases": [ 35 | "vstsFeed" 36 | ], 37 | "type": "pickList", 38 | "label": "ms-resource:loc.input.label.feedList", 39 | "defaultValue": "", 40 | "required": "true" 41 | }, 42 | { 43 | "name": "platformIndependent", 44 | "type": "boolean", 45 | "label": "ms-resource:loc.input.label.platformIndependent", 46 | "description": "Whether the cached artifact is platform independent (default is false).", 47 | "defaultValue": false, 48 | "required": "false" 49 | }, 50 | { 51 | "name": "dryRun", 52 | "type": "boolean", 53 | "label": "ms-resource:loc.input.label.dryRun", 54 | "description": "Check if the cache exists, without downloading it (default is false).", 55 | "defaultValue": false, 56 | "required": "false" 57 | }, 58 | { 59 | "name": "alias", 60 | "type": "string", 61 | "label": "ms-resource:loc.input.label.alias", 62 | "description": "An optional alias to the cache to control the name of the output variable (E.g. An alias of 'Build' sets the output variable 'CacheRestored-Build').", 63 | "defaultValue": "", 64 | "required": "false" 65 | }, 66 | { 67 | "name": "verbosity", 68 | "type": "pickList", 69 | "label": "ms-resource:loc.input.label.verbosity", 70 | "defaultValue": "None", 71 | "helpMarkDown": "ms-resource:loc.input.help.verbosity", 72 | "required": "false", 73 | "groupName": "advanced", 74 | "options": { 75 | "None": "None", 76 | "Trace": "Trace", 77 | "Debug": "Debug", 78 | "Information": "Information", 79 | "Warning": "Warning", 80 | "Error": "Error", 81 | "Critical": "Citical" 82 | } 83 | } 84 | ], 85 | "dataSourceBindings": [ 86 | { 87 | "target": "feedList", 88 | "endpointId": "tfs:feed", 89 | "endpointUrl": "{{endpoint.url}}/_apis/packaging/feeds", 90 | "resultSelector": "jsonpath:$.value[*]", 91 | "resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{name}}}\" }" 92 | } 93 | ], 94 | "execution": { 95 | "Node": { 96 | "target": "restorecache.js" 97 | } 98 | }, 99 | "messages": { 100 | "PackagesDownloadedSuccessfully": "ms-resource:loc.messages.PackagesDownloadedSuccessfully", 101 | "PackagesFailedToDownload": "ms-resource:loc.messages.PackagesFailedToDownload", 102 | "ConnectingAs": "ms-resource:loc.messages.ConnectingAs", 103 | "BuildIdentityPermissionsHint": "ms-resource:loc.messages.BuildIdentityPermissionsHint", 104 | "CouldNotFindUniversalPackagesService": "ms-resource:loc.messages.CouldNotFindUniversalPackagesService", 105 | "Error_NoValueFoundForEnvVar": "ms-resource:loc.messages.Error_NoValueFoundForEnvVar", 106 | "PackagesPublishedSuccessfully": "ms-resource:loc.messages.PackagesPublishedSuccessfully", 107 | "PackagesFailedToPublish": "ms-resource:loc.messages.PackagesFailedToPublish", 108 | "UnknownFeedType": "ms-resource:loc.messages.UnknownFeedType", 109 | "Error_NoSourceSpecifiedForPublish": "ms-resource:loc.messages.Error_NoSourceSpecifiedForPublish", 110 | "Error_NoSourceSpecifiedForDownload": "ms-resource:loc.messages.Error_NoSourceSpecifiedForDownload", 111 | "Error_UnexpectedErrorArtifactTool": "ms-resource:loc.messages.Error_UnexpectedErrorArtifactTool", 112 | "Error_UnexpectedErrorArtifactToolDownload": "ms-resource:loc.messages.Error_UnexpectedErrorArtifactToolDownload", 113 | "Info_DownloadDirectoryNotFound": "ms-resource:loc.messages.Info_DownloadDirectoryNotFound", 114 | "Info_PublishDirectoryNotFound": "ms-resource:loc.messages.Info_PublishDirectoryNotFound", 115 | "Error_CommandNotRecognized": "ms-resource:loc.messages.Error_CommandNotRecognized", 116 | "Error_NoVersionWasFoundWhichMatches": "ms-resource:loc.messages.Error_NoVersionWasFoundWhichMatches", 117 | "Error_AuthNotSupported": "ms-resource:loc.messages.Error_AuthNotSupported", 118 | "Info_DownloadingArtifactTool": "ms-resource:loc.messages.Info_DownloadingArtifactTool", 119 | "Info_Downloading": "ms-resource:loc.messages.Info_Downloading", 120 | "Info_Publishing": "ms-resource:loc.messages.Info_Publishing", 121 | "Info_UsingArtifactToolPublish": "ms-resource:loc.messages.Info_UsingArtifactToolPublish", 122 | "Info_UsingArtifactToolDownload": "ms-resource:loc.messages.Info_UsingArtifactToolDownload", 123 | "Info_ResolvedToolFromCache": "ms-resource:loc.messages.Info_ResolvedToolFromCache", 124 | "Info_UsingToolPath": "ms-resource:loc.messages.Info_UsingToolPath", 125 | "Info_UsingVersion": "ms-resource:loc.messages.Info_UsingVersion", 126 | "FailedToGetPackageMetadata": "ms-resource:loc.messages.FailedToGetPackageMetadata", 127 | "FailedToGetArtifactTool": "ms-resource:loc.messages.FailedToGetArtifactTool", 128 | "Error_UnexpectedErrorFailedToGetToolMetadata": "ms-resource:loc.messages.Error_UnexpectedErrorFailedToGetToolMetadata", 129 | "FailedToGetLatestPackageVersion": "ms-resource:loc.messages.FailedToGetLatestPackageVersion", 130 | "Warn_CredentialsNotFound": "ms-resource:loc.messages.Warn_CredentialsNotFound", 131 | "Error_UniversalPackagesNotSupportedOnPrem": "ms-resource:loc.messages.Error_UniversalPackagesNotSupportedOnPrem" 132 | } 133 | } -------------------------------------------------------------------------------- /Tasks/RestoreCacheV1/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "9aea8869-034d-4094-a6ad-880767d0686c", 3 | "name": "RestoreCache", 4 | "friendlyName": "Restore cache", 5 | "description": "Restore a folder from a cache given a specified key.", 6 | "helpMarkDown": "", 7 | "category": "Utility", 8 | "author": "Microsoft Corp", 9 | "version": { 10 | "Major": 1, 11 | "Minor": 0, 12 | "Patch": 18 13 | }, 14 | "instanceNameFormat": "Restore artifact based on: $(keyfile)", 15 | "inputs": [ 16 | { 17 | "name": "keyfile", 18 | "type": "filePath", 19 | "label": "Key file", 20 | "defaultValue": "", 21 | "required": true, 22 | "helpMarkDown": "The filename or wildcard used as the key to lookup the cache folder. For example, for npm based projects this would be '**/package-lock.json'." 23 | }, 24 | { 25 | "name": "targetfolder", 26 | "type": "filePath", 27 | "label": "Target folder", 28 | "defaultValue": "", 29 | "required": true, 30 | "helpMarkDown": "The folder/file or wildcard of items to cache. For example, node projects can cache packages with '**/node_modules, !**/node_modules/**/node_modules'." 31 | }, 32 | { 33 | "name": "feedList", 34 | "aliases": ["vstsFeed"], 35 | "type": "pickList", 36 | "label": "Feed", 37 | "defaultValue": "", 38 | "required": "true" 39 | }, 40 | { 41 | "name": "platformIndependent", 42 | "type": "boolean", 43 | "label": "Platform Independent?", 44 | "description": "Whether the cached artifact is platform independent (default is false).", 45 | "defaultValue": false, 46 | "required": "false" 47 | }, 48 | { 49 | "name": "dryRun", 50 | "type": "boolean", 51 | "label": "Dry run", 52 | "description": "Check if the cache exists, without downloading it (default is false).", 53 | "defaultValue": false, 54 | "required": "false" 55 | }, 56 | { 57 | "name": "alias", 58 | "type": "string", 59 | "label": "Cache alias", 60 | "description": "An optional alias to the cache to control the name of the output variable (E.g. An alias of 'Build' sets the output variable 'CacheRestored-Build').", 61 | "defaultValue": "", 62 | "required": "false" 63 | }, 64 | { 65 | "name": "verbosity", 66 | "type": "pickList", 67 | "label": "Verbosity", 68 | "defaultValue": "None", 69 | "helpMarkDown": "Specifies the amount of detail displayed in the output.", 70 | "required": "false", 71 | "groupName": "advanced", 72 | "options": { 73 | "None": "None", 74 | "Trace": "Trace", 75 | "Debug": "Debug", 76 | "Information": "Information", 77 | "Warning": "Warning", 78 | "Error": "Error", 79 | "Critical": "Citical" 80 | } 81 | } 82 | ], 83 | "dataSourceBindings": [ 84 | { 85 | "target": "feedList", 86 | "endpointId": "tfs:feed", 87 | "endpointUrl": "{{endpoint.url}}/_apis/packaging/feeds", 88 | "resultSelector": "jsonpath:$.value[*]", 89 | "resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{name}}}\" }" 90 | } 91 | ], 92 | "execution": { 93 | "Node": { 94 | "target": "restorecache.js" 95 | } 96 | }, 97 | "messages": { 98 | "PackagesDownloadedSuccessfully": "Package were downloaded successfully", 99 | "PackagesFailedToDownload": "Packages failed to download", 100 | "ConnectingAs": "Connecting to feeds in your Azure Pipelines/TFS project collection as '%s' [%s]", 101 | "BuildIdentityPermissionsHint": "For internal feeds, make sure the build service identity '%s' [%s] has access to the feed.", 102 | "CouldNotFindUniversalPackagesService": "Could not find the Universal Packages service. This task will not be able to authenticate to feeds hosted in your Azure Pipelines/TFS project collection.", 103 | "Error_NoValueFoundForEnvVar": "No value was found for the provided environment variable.", 104 | "PackagesPublishedSuccessfully": "Packages were published successfully", 105 | "PackagesFailedToPublish": "Packages failed to publish", 106 | "UnknownFeedType": "Unknown feed type '%s'", 107 | "Error_NoSourceSpecifiedForPublish": "No external source was specified for publish", 108 | "Error_NoSourceSpecifiedForDownload": "No external source was specified for download", 109 | "Error_UnexpectedErrorArtifactTool": "An unexpected error occurred while trying to push the package. Exit code(%s) and error(%s)", 110 | "Error_UnexpectedErrorArtifactToolDownload": "An unexpected error occurred while trying to download the package. Exit code(%s) and error(%s)", 111 | "Info_DownloadDirectoryNotFound": "Download directory not found or it did not matched the search pattern.", 112 | "Info_PublishDirectoryNotFound": "Publish directory not found or it did not matched the search pattern.", 113 | "Error_CommandNotRecognized": "The command %s was not recognized.", 114 | "Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 115 | "Error_AuthNotSupported": "Authentication using username/password not supported for Universal Packages. Please use personal access tokens.", 116 | "Info_DownloadingArtifactTool": "Downloading artifact tool from %s", 117 | "Info_Downloading": "Downloading package: %s, version: %s using feed id: %s", 118 | "Info_Publishing": "Publishing package: %s, version: %s using feed id: %s", 119 | "Info_UsingArtifactToolPublish": "Using artifact tool to publish the package", 120 | "Info_UsingArtifactToolDownload": "Using artifact tool to download the package", 121 | "Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 122 | "Info_UsingToolPath": "Using tool path: %s", 123 | "Info_UsingVersion": "Using version: %s", 124 | "FailedToGetPackageMetadata": "Failed to get package metadata.", 125 | "FailedToGetArtifactTool": "Failed to get artifact tool from service. %s", 126 | "Error_UnexpectedErrorFailedToGetToolMetadata": "Failed to get artifact tool metadata from source url %s", 127 | "FailedToGetLatestPackageVersion": "Failed to get package versions", 128 | "Warn_CredentialsNotFound": "Could not determine credentials to use for Universal Packages", 129 | "Error_UniversalPackagesNotSupportedOnPrem": "Universal Packages are not supported in Azure DevOps Server." 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/task.loc.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "50759521-9c5e-4f40-9ae7-8f9876ba9439", 3 | "name": "RestoreAndSaveCache", 4 | "friendlyName": "ms-resource:loc.friendlyName", 5 | "description": "ms-resource:loc.description", 6 | "helpMarkDown": "ms-resource:loc.helpMarkDown", 7 | "category": "Utility", 8 | "author": "Microsoft Corp", 9 | "version": { 10 | "Major": 1, 11 | "Minor": 0, 12 | "Patch": 18 13 | }, 14 | "instanceNameFormat": "ms-resource:loc.instanceNameFormat", 15 | "inputs": [ 16 | { 17 | "name": "keyfile", 18 | "type": "filePath", 19 | "label": "ms-resource:loc.input.label.keyfile", 20 | "defaultValue": "", 21 | "required": true, 22 | "helpMarkDown": "ms-resource:loc.input.help.keyfile" 23 | }, 24 | { 25 | "name": "targetfolder", 26 | "type": "filePath", 27 | "label": "ms-resource:loc.input.label.targetfolder", 28 | "defaultValue": "", 29 | "required": true, 30 | "helpMarkDown": "ms-resource:loc.input.help.targetfolder" 31 | }, 32 | { 33 | "name": "feedList", 34 | "aliases": [ 35 | "vstsFeed" 36 | ], 37 | "type": "pickList", 38 | "label": "ms-resource:loc.input.label.feedList", 39 | "defaultValue": "", 40 | "required": "true" 41 | }, 42 | { 43 | "name": "platformIndependent", 44 | "type": "boolean", 45 | "label": "ms-resource:loc.input.label.platformIndependent", 46 | "description": "Whether the cached artifact is platform independent (default is false).", 47 | "defaultValue": false, 48 | "required": "false" 49 | }, 50 | { 51 | "name": "dryRun", 52 | "type": "boolean", 53 | "label": "ms-resource:loc.input.label.dryRun", 54 | "description": "Check if the cache exists, without downloading it (default is false).", 55 | "defaultValue": false, 56 | "required": "false" 57 | }, 58 | { 59 | "name": "alias", 60 | "type": "string", 61 | "label": "ms-resource:loc.input.label.alias", 62 | "description": "An optional alias to the cache to control the name of the output variable (E.g. An alias of 'Build' sets the output variable 'CacheRestored-Build').", 63 | "defaultValue": "", 64 | "required": "false" 65 | }, 66 | { 67 | "name": "verbosity", 68 | "type": "pickList", 69 | "label": "ms-resource:loc.input.label.verbosity", 70 | "defaultValue": "None", 71 | "helpMarkDown": "ms-resource:loc.input.help.verbosity", 72 | "required": "false", 73 | "groupName": "advanced", 74 | "options": { 75 | "None": "None", 76 | "Trace": "Trace", 77 | "Debug": "Debug", 78 | "Information": "Information", 79 | "Warning": "Warning", 80 | "Error": "Error", 81 | "Critical": "Citical" 82 | } 83 | } 84 | ], 85 | "dataSourceBindings": [ 86 | { 87 | "target": "feedList", 88 | "endpointId": "tfs:feed", 89 | "endpointUrl": "{{endpoint.url}}/_apis/packaging/feeds", 90 | "resultSelector": "jsonpath:$.value[*]", 91 | "resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{name}}}\" }" 92 | } 93 | ], 94 | "execution": { 95 | "Node": { 96 | "target": "restorecache.js" 97 | } 98 | }, 99 | "postjobexecution": { 100 | "Node": { 101 | "target": "savecache.js" 102 | } 103 | }, 104 | "messages": { 105 | "PackagesDownloadedSuccessfully": "ms-resource:loc.messages.PackagesDownloadedSuccessfully", 106 | "PackagesFailedToDownload": "ms-resource:loc.messages.PackagesFailedToDownload", 107 | "ConnectingAs": "ms-resource:loc.messages.ConnectingAs", 108 | "BuildIdentityPermissionsHint": "ms-resource:loc.messages.BuildIdentityPermissionsHint", 109 | "CouldNotFindUniversalPackagesService": "ms-resource:loc.messages.CouldNotFindUniversalPackagesService", 110 | "Error_NoValueFoundForEnvVar": "ms-resource:loc.messages.Error_NoValueFoundForEnvVar", 111 | "PackagesPublishedSuccessfully": "ms-resource:loc.messages.PackagesPublishedSuccessfully", 112 | "PackagesFailedToPublish": "ms-resource:loc.messages.PackagesFailedToPublish", 113 | "UnknownFeedType": "ms-resource:loc.messages.UnknownFeedType", 114 | "Error_NoSourceSpecifiedForPublish": "ms-resource:loc.messages.Error_NoSourceSpecifiedForPublish", 115 | "Error_NoSourceSpecifiedForDownload": "ms-resource:loc.messages.Error_NoSourceSpecifiedForDownload", 116 | "Error_UnexpectedErrorArtifactTool": "ms-resource:loc.messages.Error_UnexpectedErrorArtifactTool", 117 | "Error_UnexpectedErrorArtifactToolDownload": "ms-resource:loc.messages.Error_UnexpectedErrorArtifactToolDownload", 118 | "Info_DownloadDirectoryNotFound": "ms-resource:loc.messages.Info_DownloadDirectoryNotFound", 119 | "Info_PublishDirectoryNotFound": "ms-resource:loc.messages.Info_PublishDirectoryNotFound", 120 | "Error_CommandNotRecognized": "ms-resource:loc.messages.Error_CommandNotRecognized", 121 | "Error_NoVersionWasFoundWhichMatches": "ms-resource:loc.messages.Error_NoVersionWasFoundWhichMatches", 122 | "Error_AuthNotSupported": "ms-resource:loc.messages.Error_AuthNotSupported", 123 | "Info_DownloadingArtifactTool": "ms-resource:loc.messages.Info_DownloadingArtifactTool", 124 | "Info_Downloading": "ms-resource:loc.messages.Info_Downloading", 125 | "Info_Publishing": "ms-resource:loc.messages.Info_Publishing", 126 | "Info_UsingArtifactToolPublish": "ms-resource:loc.messages.Info_UsingArtifactToolPublish", 127 | "Info_UsingArtifactToolDownload": "ms-resource:loc.messages.Info_UsingArtifactToolDownload", 128 | "Info_ResolvedToolFromCache": "ms-resource:loc.messages.Info_ResolvedToolFromCache", 129 | "Info_UsingToolPath": "ms-resource:loc.messages.Info_UsingToolPath", 130 | "Info_UsingVersion": "ms-resource:loc.messages.Info_UsingVersion", 131 | "FailedToGetPackageMetadata": "ms-resource:loc.messages.FailedToGetPackageMetadata", 132 | "FailedToGetArtifactTool": "ms-resource:loc.messages.FailedToGetArtifactTool", 133 | "Error_UnexpectedErrorFailedToGetToolMetadata": "ms-resource:loc.messages.Error_UnexpectedErrorFailedToGetToolMetadata", 134 | "FailedToGetLatestPackageVersion": "ms-resource:loc.messages.FailedToGetLatestPackageVersion", 135 | "Warn_CredentialsNotFound": "ms-resource:loc.messages.Warn_CredentialsNotFound", 136 | "Error_UniversalPackagesNotSupportedOnPrem": "ms-resource:loc.messages.Error_UniversalPackagesNotSupportedOnPrem" 137 | } 138 | } -------------------------------------------------------------------------------- /Tasks/RestoreAndSaveCacheV1/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "50759521-9c5e-4f40-9ae7-8f9876ba9439", 3 | "name": "RestoreAndSaveCache", 4 | "friendlyName": "Restore and save cache", 5 | "description": "Restores and saves a folder given a specified key.", 6 | "helpMarkDown": "", 7 | "category": "Utility", 8 | "author": "Microsoft Corp", 9 | "version": { 10 | "Major": 1, 11 | "Minor": 0, 12 | "Patch": 18 13 | }, 14 | "instanceNameFormat": "Restore and save artifact based on: $(keyfile)", 15 | "inputs": [ 16 | { 17 | "name": "keyfile", 18 | "type": "filePath", 19 | "label": "Key file", 20 | "defaultValue": "", 21 | "required": true, 22 | "helpMarkDown": "The filename or wildcard used as the key to lookup the cache folder. For example, for npm based projects this would be '**/package-lock.json'." 23 | }, 24 | { 25 | "name": "targetfolder", 26 | "type": "filePath", 27 | "label": "Target folder", 28 | "defaultValue": "", 29 | "required": true, 30 | "helpMarkDown": "The folder/file or wildcard of items to cache. For example, node projects can cache packages with '**/node_modules, !**/node_modules/**/node_modules'." 31 | }, 32 | { 33 | "name": "feedList", 34 | "aliases": ["vstsFeed"], 35 | "type": "pickList", 36 | "label": "Feed", 37 | "defaultValue": "", 38 | "required": "true" 39 | }, 40 | { 41 | "name": "platformIndependent", 42 | "type": "boolean", 43 | "label": "Platform Independent?", 44 | "description": "Whether the cached artifact is platform independent (default is false).", 45 | "defaultValue": false, 46 | "required": "false" 47 | }, 48 | { 49 | "name": "dryRun", 50 | "type": "boolean", 51 | "label": "Dry run", 52 | "description": "Check if the cache exists, without downloading it (default is false).", 53 | "defaultValue": false, 54 | "required": "false" 55 | }, 56 | { 57 | "name": "alias", 58 | "type": "string", 59 | "label": "Cache alias", 60 | "description": "An optional alias to the cache to control the name of the output variable (E.g. An alias of 'Build' sets the output variable 'CacheRestored-Build').", 61 | "defaultValue": "", 62 | "required": "false" 63 | }, 64 | { 65 | "name": "verbosity", 66 | "type": "pickList", 67 | "label": "Verbosity", 68 | "defaultValue": "None", 69 | "helpMarkDown": "Specifies the amount of detail displayed in the output.", 70 | "required": "false", 71 | "groupName": "advanced", 72 | "options": { 73 | "None": "None", 74 | "Trace": "Trace", 75 | "Debug": "Debug", 76 | "Information": "Information", 77 | "Warning": "Warning", 78 | "Error": "Error", 79 | "Critical": "Citical" 80 | } 81 | } 82 | ], 83 | "dataSourceBindings": [ 84 | { 85 | "target": "feedList", 86 | "endpointId": "tfs:feed", 87 | "endpointUrl": "{{endpoint.url}}/_apis/packaging/feeds", 88 | "resultSelector": "jsonpath:$.value[*]", 89 | "resultTemplate": "{ \"Value\" : \"{{{id}}}\", \"DisplayValue\" : \"{{{name}}}\" }" 90 | } 91 | ], 92 | "execution": { 93 | "Node": { 94 | "target": "restorecache.js" 95 | } 96 | }, 97 | "postjobexecution": { 98 | "Node": { 99 | "target": "savecache.js" 100 | } 101 | }, 102 | "messages": { 103 | "PackagesDownloadedSuccessfully": "Package were downloaded successfully", 104 | "PackagesFailedToDownload": "Packages failed to download", 105 | "ConnectingAs": "Connecting to feeds in your Azure Pipelines/TFS project collection as '%s' [%s]", 106 | "BuildIdentityPermissionsHint": "For internal feeds, make sure the build service identity '%s' [%s] has access to the feed.", 107 | "CouldNotFindUniversalPackagesService": "Could not find the Universal Packages service. This task will not be able to authenticate to feeds hosted in your Azure Pipelines/TFS project collection.", 108 | "Error_NoValueFoundForEnvVar": "No value was found for the provided environment variable.", 109 | "PackagesPublishedSuccessfully": "Packages were published successfully", 110 | "PackagesFailedToPublish": "Packages failed to publish", 111 | "UnknownFeedType": "Unknown feed type '%s'", 112 | "Error_NoSourceSpecifiedForPublish": "No external source was specified for publish", 113 | "Error_NoSourceSpecifiedForDownload": "No external source was specified for download", 114 | "Error_UnexpectedErrorArtifactTool": "An unexpected error occurred while trying to push the package. Exit code(%s) and error(%s)", 115 | "Error_UnexpectedErrorArtifactToolDownload": "An unexpected error occurred while trying to download the package. Exit code(%s) and error(%s)", 116 | "Info_DownloadDirectoryNotFound": "Download directory not found or it did not matched the search pattern.", 117 | "Info_PublishDirectoryNotFound": "Publish directory not found or it did not matched the search pattern.", 118 | "Error_CommandNotRecognized": "The command %s was not recognized.", 119 | "Error_NoVersionWasFoundWhichMatches": "No version was found which matches the input %s", 120 | "Error_AuthNotSupported": "Authentication using username/password not supported for Universal Packages. Please use personal access tokens.", 121 | "Info_DownloadingArtifactTool": "Downloading artifact tool from %s", 122 | "Info_Downloading": "Downloading package: %s, version: %s using feed id: %s", 123 | "Info_Publishing": "Publishing package: %s, version: %s using feed id: %s", 124 | "Info_UsingArtifactToolPublish": "Using artifact tool to publish the package", 125 | "Info_UsingArtifactToolDownload": "Using artifact tool to download the package", 126 | "Info_ResolvedToolFromCache": "Resolved from tool cache: %s", 127 | "Info_UsingToolPath": "Using tool path: %s", 128 | "Info_UsingVersion": "Using version: %s", 129 | "FailedToGetPackageMetadata": "Failed to get package metadata.", 130 | "FailedToGetArtifactTool": "Failed to get artifact tool from service. %s", 131 | "Error_UnexpectedErrorFailedToGetToolMetadata": "Failed to get artifact tool metadata from source url %s", 132 | "FailedToGetLatestPackageVersion": "Failed to get package versions", 133 | "Warn_CredentialsNotFound": "Could not determine credentials to use for Universal Packages", 134 | "Error_UniversalPackagesNotSupportedOnPrem": "Universal Packages are not supported in Azure DevOps Server." 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/ArtifactToolUtilities.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | // Placed as a separate file for the purpose of unit testing 5 | import AdmZip = require('adm-zip'); 6 | import os = require("os"); 7 | import * as path from "path"; 8 | import * as semver from "semver"; 9 | import * as pkgLocationUtils from "./locationUtilities"; 10 | import * as tl from "azure-pipelines-task-lib"; 11 | import * as toollib from "azure-pipelines-tool-lib/tool"; 12 | 13 | export function getArtifactToolLocation(dirName: string): string { 14 | let toolPath: string = path.join(dirName, "ArtifactTool.exe"); 15 | if (tl.osType() !== "Windows_NT") { 16 | toolPath = path.join(dirName, "artifacttool"); 17 | } 18 | return toolPath; 19 | } 20 | 21 | function _createExtractFolder(dest?: string): string { 22 | if (!dest) { 23 | // create a temp dir 24 | dest = path.join(tl.getVariable("Agent.TempDirectory"), "artifactTool"); 25 | } 26 | tl.mkdirP(dest); 27 | return dest; 28 | } 29 | 30 | export async function extractZip(file: string): Promise { 31 | if (!file) { 32 | throw new Error("parameter 'file' is required"); 33 | } 34 | const dest = _createExtractFolder(); 35 | const zip = new AdmZip(file); 36 | zip.extractAllTo(dest, true); 37 | return dest; 38 | } 39 | 40 | export async function getArtifactToolFromService(serviceUri: string, accessToken: string, toolName: string) { 41 | const overrideArtifactToolPath = tl.getVariable("UPack.OverrideArtifactToolPath"); 42 | if (overrideArtifactToolPath != null) { 43 | return getArtifactToolLocation(overrideArtifactToolPath); 44 | } 45 | 46 | let osName = tl.osType(); 47 | let arch = os.arch(); 48 | if (osName === "Windows_NT") { 49 | osName = "windows"; 50 | } 51 | if (arch === "x64") { 52 | arch = "amd64"; 53 | } 54 | 55 | const blobstoreAreaName = "clienttools"; 56 | const blobstoreAreaId = "187ec90d-dd1e-4ec6-8c57-937d979261e5"; 57 | const ApiVersion = "5.0-preview"; 58 | 59 | const blobstoreConnection = pkgLocationUtils.getWebApiWithProxy(serviceUri, accessToken); 60 | 61 | try { 62 | const artifactToolGetUrl = await blobstoreConnection.vsoClient.getVersioningData(ApiVersion, 63 | blobstoreAreaName, blobstoreAreaId, { toolName }, {osName, arch}); 64 | 65 | const artifactToolUri = await blobstoreConnection.rest.get(artifactToolGetUrl.requestUrl); 66 | 67 | if (artifactToolUri.statusCode !== 200) { 68 | tl.debug(tl.loc("Error_UnexpectedErrorFailedToGetToolMetadata", artifactToolUri.toString())); 69 | throw new Error(tl.loc("Error_UnexpectedErrorFailedToGetToolMetadata", artifactToolGetUrl.requestUrl)); 70 | } 71 | 72 | let artifactToolPath = toollib.findLocalTool(toolName, artifactToolUri.result['version']); 73 | if (!artifactToolPath) { 74 | tl.debug(tl.loc("Info_DownloadingArtifactTool", artifactToolUri.result['uri'])); 75 | 76 | const zippedToolsDir: string = await toollib.downloadTool(artifactToolUri.result['uri']); 77 | 78 | tl.debug("Downloaded zipped artifact tool to " + zippedToolsDir); 79 | const unzippedToolsDir = await extractZip(zippedToolsDir); 80 | 81 | artifactToolPath = await toollib.cacheDir(unzippedToolsDir, "ArtifactTool", artifactToolUri.result['version']); 82 | } else { 83 | tl.debug(tl.loc("Info_ResolvedToolFromCache", artifactToolPath)); 84 | } 85 | return getArtifactToolLocation(artifactToolPath); 86 | } catch (err) { 87 | tl.warning(err); 88 | // TODO: Should return null? 89 | // tl.setResult(tl.TaskResult.Failed, tl.loc("FailedToGetArtifactTool", err)); 90 | return null; 91 | } 92 | } 93 | 94 | export function getVersionUtility(versionRadio: string, highestVersion: string): string { 95 | switch (versionRadio) { 96 | case "patch": 97 | return semver.inc(highestVersion, "patch"); 98 | case "minor": 99 | return semver.inc(highestVersion, "minor"); 100 | case "major": 101 | return semver.inc(highestVersion, "major"); 102 | default: 103 | return null; 104 | } 105 | } 106 | 107 | export async function getPackageNameFromId(serviceUri: string, accessToken: string, feedId: string, packageId: string): Promise { 108 | const ApiVersion = "3.0-preview.1"; 109 | const PackagingAreaName = "Packaging"; 110 | const PackageAreaId = "7a20d846-c929-4acc-9ea2-0d5a7df1b197"; 111 | 112 | const feedConnection = pkgLocationUtils.getWebApiWithProxy(serviceUri, accessToken); 113 | 114 | // Getting url for feeds version API 115 | const packageUrl = await new Promise((resolve, reject) => { 116 | const getVersioningDataPromise = feedConnection.vsoClient.getVersioningData(ApiVersion, PackagingAreaName, PackageAreaId, { feedId, packageId }); 117 | getVersioningDataPromise.then(result => { 118 | return resolve(result.requestUrl); 119 | }); 120 | getVersioningDataPromise.catch(error => { 121 | return reject(error); 122 | }); 123 | }); 124 | 125 | // Return the user input incase of failure 126 | try { 127 | const response = await feedConnection.rest.get(packageUrl); 128 | if (response.statusCode === 200 && response.result['name']) { 129 | return response.result['name']; 130 | } 131 | return packageId; 132 | } catch (err) { 133 | return packageId; 134 | } 135 | } 136 | 137 | export async function getHighestPackageVersionFromFeed(serviceUri: string, accessToken: string, feedId: string, packageName: string): Promise { 138 | const ApiVersion = "3.0-preview.1"; 139 | const PackagingAreaName = "Packaging"; 140 | const PackageAreaId = "7a20d846-c929-4acc-9ea2-0d5a7df1b197"; 141 | 142 | const feedConnection = pkgLocationUtils.getWebApiWithProxy(serviceUri, accessToken); 143 | 144 | // Getting url for feeds version API 145 | const packageUrl = await new Promise((resolve, reject) => { 146 | const getVersioningDataPromise = feedConnection.vsoClient.getVersioningData(ApiVersion, PackagingAreaName, PackageAreaId, { feedId }, {packageNameQuery: packageName, protocolType: "upack", includeDeleted: "true", includeUrls: "false"}); 147 | getVersioningDataPromise.then(result => { 148 | return resolve(result.requestUrl); 149 | }); 150 | getVersioningDataPromise.catch(error => { 151 | return reject(error); 152 | }); 153 | }); 154 | 155 | const versionResponse = await new Promise((resolve, reject) => { 156 | const responsePromise = feedConnection.rest.get(packageUrl); 157 | responsePromise.then(result => { 158 | if (result.result['count'] === 0) { 159 | return resolve("0.0.0"); 160 | } else { 161 | result.result['value'].forEach(element => { 162 | if (element.name === packageName.toLowerCase()) { 163 | return resolve(element.versions[0].version); 164 | } 165 | }); 166 | } 167 | }); 168 | responsePromise.catch(error => { 169 | return reject(error); 170 | }); 171 | }); 172 | 173 | return versionResponse; 174 | } 175 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/provenance.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as tl from "azure-pipelines-task-lib"; 5 | 6 | import * as VsoBaseInterfaces from 'azure-devops-node-api/interfaces/common/VsoBaseInterfaces'; 7 | import { ClientVersioningData } from 'azure-devops-node-api/VsoClient'; 8 | import vstsClientBases = require("azure-devops-node-api/ClientApiBases"); 9 | 10 | import * as restclient from 'typed-rest-client/RestClient'; 11 | 12 | export interface SessionRequest { 13 | /** 14 | * Generic property bag to store data about the session 15 | */ 16 | data: { [key: string] : string; }; 17 | /** 18 | * The feed name or id for the session 19 | */ 20 | feed: string; 21 | /** 22 | * The type of session If a known value is provided, the Data dictionary will be validated for the presence of properties required by that type 23 | */ 24 | source: string; 25 | } 26 | 27 | export interface SessionResponse { 28 | /** 29 | * The identifier for the session 30 | */ 31 | sessionId: string; 32 | } 33 | 34 | export class ProvenanceHelper { 35 | /* Creates a session request with default data provided by the build variables */ 36 | public static CreateSessionRequest(feedId: string): SessionRequest { 37 | 38 | var releaseId = tl.getVariable("Release.ReleaseId"); 39 | if (releaseId != null) { 40 | return ProvenanceHelper.CreateReleaseSessionRequest(feedId, releaseId); 41 | } 42 | 43 | var buildId = tl.getVariable("Build.BuildId"); 44 | if (buildId != null) { 45 | return ProvenanceHelper.CreateBuildSessionRequest(feedId, buildId); 46 | } 47 | 48 | throw new Error("Could not resolve Release.ReleaseId or Build.BuildId"); 49 | } 50 | 51 | public static async GetSessionId( 52 | feedId: string, 53 | protocol: string, 54 | baseUrl: string, 55 | handlers: VsoBaseInterfaces.IRequestHandler[], 56 | options: VsoBaseInterfaces.IRequestOptions): Promise { 57 | 58 | const publishPackageMetadata = tl.getInput("publishPackageMetadata"); 59 | let shouldCreateSession = publishPackageMetadata && publishPackageMetadata.toLowerCase() == 'true'; 60 | if (shouldCreateSession) { 61 | const useSessionEnabled = tl.getVariable("Packaging.SavePublishMetadata"); 62 | shouldCreateSession = shouldCreateSession && !(useSessionEnabled && useSessionEnabled.toLowerCase() == 'false') 63 | } 64 | if (shouldCreateSession) { 65 | tl.debug("Creating provenance session to save pipeline metadata. This can be disabled in the task settings, or by setting build variable Packaging.SavePublishMetadata to false"); 66 | const prov = new ProvenanceApi(baseUrl, handlers, options); 67 | const sessionRequest = ProvenanceHelper.CreateSessionRequest(feedId); 68 | try { 69 | const session = await prov.createSession(sessionRequest, protocol); 70 | return session.sessionId; 71 | } catch (error) { 72 | tl.warning(tl.loc("Warning_SessionCreationFailed", JSON.stringify(error))); 73 | } 74 | } 75 | return feedId; 76 | } 77 | 78 | private static CreateReleaseSessionRequest(feedId: string, releaseId: string): SessionRequest { 79 | let releaseData = { 80 | "System.CollectionId": tl.getVariable("System.CollectionId"), 81 | "System.TeamProjectId": tl.getVariable("System.TeamProjectId"), 82 | "Release.ReleaseId": releaseId, 83 | "Release.ReleaseName": tl.getVariable("Release.ReleaseName"), 84 | "Release.DefinitionName": tl.getVariable("Release.DefinitionName"), 85 | "Release.DefinitionId": tl.getVariable("Release.DefinitionId") 86 | } 87 | 88 | var sessionRequest: SessionRequest = { 89 | feed: feedId, 90 | source: "InternalRelease", 91 | data: releaseData 92 | } 93 | 94 | return sessionRequest; 95 | } 96 | 97 | private static CreateBuildSessionRequest(feedId: string, buildId: string): SessionRequest { 98 | let buildData = { 99 | "System.CollectionId": tl.getVariable("System.CollectionId"), 100 | "System.DefinitionId": tl.getVariable("System.DefinitionId"), 101 | "System.TeamProjectId": tl.getVariable("System.TeamProjectId"), 102 | "Build.BuildId": buildId, 103 | "Build.BuildNumber": tl.getVariable("Build.BuildNumber"), 104 | "Build.DefinitionName": tl.getVariable("Build.DefinitionName"), 105 | "Build.Repository.Name": tl.getVariable("Build.Repository.Name"), 106 | "Build.Repository.Provider": tl.getVariable("Build.Repository.Provider"), 107 | "Build.Repository.Id": tl.getVariable("Build.Repository.Id"), 108 | "Build.Repository.Uri": tl.getVariable("Build.Repository.Uri"), 109 | "Build.SourceBranch": tl.getVariable("Build.SourceBranch"), 110 | "Build.SourceBranchName": tl.getVariable("Build.SourceBranchName"), 111 | "Build.SourceVersion": tl.getVariable("Build.SourceVersion") 112 | } 113 | 114 | var sessionRequest: SessionRequest = { 115 | feed: feedId, 116 | source: "InternalBuild", 117 | data: buildData 118 | } 119 | 120 | return sessionRequest; 121 | } 122 | } 123 | 124 | class ProvenanceApi extends vstsClientBases.ClientApiBase { 125 | constructor(baseUrl: string, handlers: VsoBaseInterfaces.IRequestHandler[], options?: VsoBaseInterfaces.IRequestOptions) { 126 | super(baseUrl, handlers, "node-packageprovenance-api", options); 127 | } 128 | 129 | /** 130 | * Creates a session, a wrapper around a feed that can store additional metadata on the packages published to the session. 131 | * 132 | * @param {SessionRequest} sessionRequest - The feed and metadata for the session 133 | * @param {string} protocol - The protocol that the session will target 134 | */ 135 | public async createSession( 136 | sessionRequest: SessionRequest, 137 | protocol: string 138 | ): Promise { 139 | 140 | return new Promise(async (resolve, reject) => { 141 | 142 | let routeValues: any = { 143 | protocol: protocol 144 | }; 145 | 146 | try { 147 | let verData: ClientVersioningData = await this.vsoClient.getVersioningData( 148 | "5.0-preview.1", 149 | "Provenance", 150 | "503B4E54-EBF4-4D04-8EEE-21C00823C2AC", 151 | routeValues); 152 | 153 | let url: string = verData.requestUrl; 154 | 155 | let options: restclient.IRequestOptions = this.createRequestOptions('application/json', 156 | verData.apiVersion); 157 | 158 | let res: restclient.IRestResponse; 159 | res = await this.rest.create(url, sessionRequest, options); 160 | let ret = this.formatResponse(res.result, 161 | null, 162 | false); 163 | 164 | resolve(ret); 165 | } 166 | catch (err) { 167 | reject(err); 168 | } 169 | }); 170 | } 171 | } 172 | 173 | -------------------------------------------------------------------------------- /Tasks/Common/packaging-common/locationUtilities.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | import * as vsts from "azure-devops-node-api"; 5 | import * as interfaces from "azure-devops-node-api/interfaces/common/VSSInterfaces"; 6 | import * as tl from "azure-pipelines-task-lib/task"; 7 | import { IRequestOptions } from "azure-devops-node-api/interfaces/common/VsoBaseInterfaces"; 8 | 9 | import * as provenance from "./provenance"; 10 | 11 | export enum ProtocolType { 12 | NuGet, 13 | Maven, 14 | Npm, 15 | PyPi, 16 | } 17 | 18 | export enum RegistryType { 19 | npm, 20 | NuGetV2, 21 | NuGetV3, 22 | PyPiSimple, 23 | PyPiUpload, 24 | } 25 | 26 | export interface PackagingLocation { 27 | PackagingUris: string[]; 28 | DefaultPackagingUri: string; 29 | } 30 | 31 | // Getting service urls from resource areas api 32 | export async function getServiceUriFromAreaId( 33 | serviceUri: string, 34 | accessToken: string, 35 | areaId: string 36 | ): Promise { 37 | const serverType = tl.getVariable("System.ServerType"); 38 | if (!serverType || serverType.toLowerCase() !== "hosted") { 39 | return serviceUri; 40 | } 41 | 42 | const webApi = getWebApiWithProxy(serviceUri, accessToken); 43 | const locationApi = await webApi.getLocationsApi(); 44 | 45 | tl.debug(`Getting URI for area ID ${areaId} from ${serviceUri}`); 46 | try { 47 | const serviceUriFromArea = await locationApi.getResourceArea(areaId); 48 | console.log("got service url from area"); 49 | return serviceUriFromArea.locationUrl; 50 | } catch (error) { 51 | throw new Error(error); 52 | } 53 | } 54 | 55 | export async function getNuGetUriFromBaseServiceUri( 56 | serviceUri: string, 57 | accesstoken: string 58 | ): Promise { 59 | const nugetAreaId = "B3BE7473-68EA-4A81-BFC7-9530BAAA19AD"; 60 | 61 | return getServiceUriFromAreaId(serviceUri, accesstoken, nugetAreaId); 62 | } 63 | 64 | // Feeds url from location service 65 | export async function getFeedUriFromBaseServiceUri( 66 | serviceUri: string, 67 | accesstoken: string 68 | ): Promise { 69 | const feedAreaId = "7ab4e64e-c4d8-4f50-ae73-5ef2e21642a5"; 70 | 71 | return getServiceUriFromAreaId(serviceUri, accesstoken, feedAreaId); 72 | } 73 | 74 | export async function getBlobstoreUriFromBaseServiceUri( 75 | serviceUri: string, 76 | accesstoken: string 77 | ): Promise { 78 | const blobAreaId = "5294ef93-12a1-4d13-8671-9d9d014072c8"; 79 | 80 | return getServiceUriFromAreaId(serviceUri, accesstoken, blobAreaId); 81 | } 82 | 83 | /** 84 | * PackagingLocation.PackagingUris: 85 | * The first URI will always be the TFS collection URI 86 | * The second URI, if existent, will be Packaging's default access point 87 | * The remaining URI's will be alternate Packaging's access points 88 | */ 89 | export async function getPackagingUris( 90 | protocolType: ProtocolType 91 | ): Promise { 92 | tl.debug("Getting Packaging service access points"); 93 | const collectionUrl = tl.getVariable("System.TeamFoundationCollectionUri"); 94 | 95 | const pkgLocation: PackagingLocation = { 96 | PackagingUris: [collectionUrl], 97 | DefaultPackagingUri: collectionUrl, 98 | }; 99 | 100 | const serverType = tl.getVariable("System.ServerType"); 101 | if (!serverType || serverType.toLowerCase() !== "hosted") { 102 | return pkgLocation; 103 | } 104 | 105 | const accessToken = getSystemAccessToken(); 106 | const areaId = getAreaIdForProtocol(protocolType); 107 | 108 | const serviceUri = await getServiceUriFromAreaId( 109 | collectionUrl, 110 | accessToken, 111 | areaId 112 | ); 113 | 114 | const webApi = getWebApiWithProxy(serviceUri); 115 | 116 | const locationApi = await webApi.getLocationsApi(); 117 | 118 | tl.debug("Acquiring Packaging endpoints from " + serviceUri); 119 | return locationApi 120 | .getConnectionData(interfaces.ConnectOptions.IncludeServices) 121 | .then(connectionData => { 122 | tl.debug("Successfully acquired the connection data"); 123 | const defaultAccessPoint: string = connectionData.locationServiceData.accessMappings.find( 124 | mapping => 125 | mapping.moniker === 126 | connectionData.locationServiceData.defaultAccessMappingMoniker 127 | ).accessPoint; 128 | 129 | pkgLocation.DefaultPackagingUri = defaultAccessPoint; 130 | pkgLocation.PackagingUris.push(defaultAccessPoint); 131 | pkgLocation.PackagingUris = pkgLocation.PackagingUris.concat( 132 | connectionData.locationServiceData.accessMappings.map(mapping => { 133 | return mapping.accessPoint; 134 | }) 135 | ); 136 | 137 | tl.debug("Acquired location"); 138 | tl.debug(JSON.stringify(pkgLocation)); 139 | return pkgLocation; 140 | }) 141 | .catch(error => { 142 | tl.debug("An error occurred while acquiring the connection data"); 143 | tl.debug(JSON.stringify(error)); 144 | return pkgLocation; 145 | }); 146 | } 147 | 148 | export function getSystemAccessToken(): string { 149 | tl.debug("Getting credentials for local feeds"); 150 | const auth = tl.getEndpointAuthorization("SYSTEMVSSCONNECTION", false); 151 | if (auth.scheme === "OAuth") { 152 | tl.debug("Got auth token"); 153 | return auth.parameters.AccessToken; 154 | } else { 155 | tl.warning("Could not determine credentials to use"); 156 | } 157 | } 158 | 159 | function getAreaIdForProtocol(protocolType: ProtocolType): string { 160 | switch (protocolType) { 161 | case ProtocolType.Maven: 162 | return "6F7F8C07-FF36-473C-BCF3-BD6CC9B6C066"; 163 | case ProtocolType.Npm: 164 | return "4C83CFC1-F33A-477E-A789-29D38FFCA52E"; 165 | default: 166 | case ProtocolType.NuGet: 167 | return "B3BE7473-68EA-4A81-BFC7-9530BAAA19AD"; 168 | } 169 | } 170 | 171 | export function getWebApiWithProxy( 172 | serviceUri: string, 173 | accessToken?: string 174 | ): vsts.WebApi { 175 | if (!accessToken) { 176 | accessToken = getSystemAccessToken(); 177 | } 178 | const credentialHandler = vsts.getBasicHandler("vsts", accessToken); 179 | 180 | const options: IRequestOptions = { 181 | proxy: tl.getHttpProxyConfiguration(serviceUri), 182 | }; 183 | return new vsts.WebApi(serviceUri, credentialHandler, options); 184 | } 185 | 186 | interface RegistryLocation { 187 | apiVersion: string; 188 | area: string; 189 | locationId: string; 190 | } 191 | 192 | export async function getFeedRegistryUrl( 193 | packagingUrl: string, 194 | registryType: RegistryType, 195 | feedId: string, 196 | accessToken?: string, 197 | useSession?: boolean 198 | ): Promise { 199 | let loc: RegistryLocation; 200 | switch (registryType) { 201 | case RegistryType.npm: 202 | loc = { 203 | apiVersion: "3.0-preview.1", 204 | area: "npm", 205 | locationId: "D9B75B07-F1D9-4A67-AAA6-A4D9E66B3352", 206 | }; 207 | break; 208 | case RegistryType.NuGetV2: 209 | loc = { 210 | apiVersion: "3.0-preview.1", 211 | area: "nuget", 212 | locationId: "5D6FC3B3-EF78-4342-9B6E-B3799C866CFA", 213 | }; 214 | break; 215 | case RegistryType.PyPiSimple: 216 | loc = { 217 | apiVersion: "5.0", 218 | area: "pypi", 219 | locationId: "93377A2C-F5FB-48B9-A8DC-7781441CABF1", 220 | }; 221 | break; 222 | case RegistryType.PyPiUpload: 223 | loc = { 224 | apiVersion: "5.0", 225 | area: "pypi", 226 | locationId: "C7A75C1B-08AC-4B11-B468-6C7EF835C85E", 227 | }; 228 | break; 229 | default: 230 | case RegistryType.NuGetV3: 231 | loc = { 232 | apiVersion: "3.0-preview.1", 233 | area: "nuget", 234 | locationId: "9D3A4E8E-2F8F-4AE1-ABC2-B461A51CB3B3", 235 | }; 236 | break; 237 | } 238 | 239 | tl.debug("Getting registry url from " + packagingUrl); 240 | 241 | const vssConnection = getWebApiWithProxy(packagingUrl, accessToken); 242 | 243 | let sessionId = feedId; 244 | if (useSession) { 245 | sessionId = await provenance.ProvenanceHelper.GetSessionId( 246 | feedId, 247 | loc.area /* protocol */, 248 | vssConnection.serverUrl, 249 | [vssConnection.authHandler], 250 | vssConnection.options 251 | ); 252 | } 253 | 254 | const data = await Retry( 255 | async () => { 256 | return await vssConnection.vsoClient.getVersioningData( 257 | loc.apiVersion, 258 | loc.area, 259 | loc.locationId, 260 | { feedId: sessionId } 261 | ); 262 | }, 263 | 4, 264 | 100 265 | ); 266 | 267 | tl.debug("Feed registry url: " + data.requestUrl); 268 | return data.requestUrl; 269 | } 270 | 271 | // This should be replaced when retry is implemented in vso client. 272 | async function Retry( 273 | cb: () => Promise, 274 | max_retry: number, 275 | retry_delay: number 276 | ): Promise { 277 | try { 278 | return await cb(); 279 | } catch (exception) { 280 | tl.debug(JSON.stringify(exception)); 281 | if (max_retry > 0) { 282 | tl.debug("Waiting " + retry_delay + "ms..."); 283 | await delay(retry_delay); 284 | tl.debug("Retrying..."); 285 | return await Retry(cb, max_retry - 1, retry_delay * 2); 286 | } else { 287 | throw new Error(exception); 288 | } 289 | } 290 | } 291 | function delay(delayMs: number) { 292 | return new Promise(function(resolve) { 293 | setTimeout(resolve, delayMs); 294 | }); 295 | } 296 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Attention 2 | 3 | The tasks contained within this repository are not maintained at this time. Instead, please use the built-in [Pipeline Caching](https://docs.microsoft.com/en-us/azure/devops/pipelines/release/caching?view=azure-devops) tasks for caching intermediate artifacts in Azure Pipelines. 4 | 5 | # Azure Pipelines Artifact Caching Tasks 6 | 7 | [![Build Status](https://dev.azure.com/1es-cat/azure-pipelines-artifact-caching-tasks/_apis/build/status/Microsoft.azure-pipelines-artifact-caching-tasks?branchName=master)](https://dev.azure.com/1es-cat/azure-pipelines-artifact-caching-tasks/_build/latest?definitionId=17&branchName=master) [![Release status](https://vsrm.dev.azure.com/1es-cat/_apis/public/Release/badge/73af267c-80da-42c5-b634-ef63bb6d61fc/1/1)](https://dev.azure.com/1es-cat/azure-pipelines-artifact-caching-tasks/_release?definitionId=1) 8 | 9 | This repo contains the tasks that enable the caching of intermediate artifacts from an Azure Pipelines build using Universal Artifacts. 10 | 11 | ## How to use 12 | 13 | This build task is meant to add an easy way to provide caching of intermediate build artifacts. To demonstrate, let's examine the following build definition snippet: 14 | 15 | ```yaml 16 | - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 17 | inputs: 18 | keyfile: "**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" 19 | targetfolder: "**/node_modules, !**/node_modules/**/node_modules" 20 | vstsFeed: "$(ArtifactFeed)" 21 | 22 | - script: | 23 | yarn install 24 | displayName: Install Dependencies 25 | 26 | - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 27 | inputs: 28 | keyfile: "**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" 29 | targetfolder: "**/node_modules, !**/node_modules/**/node_modules" 30 | vstsFeed: "$(ArtifactFeed)" 31 | ``` 32 | 33 | Conceptually, this snippet creates a lookup key from the `keyfile` argument and checks the `vstsFeed` for a matching entry. If one exists, it will be downloaded and unpacked. After more `node_modules` are restored via `yarn` the `SaveCache` task runs to create a cache entry if it wasn't available previously (if a cache entry was downloaded, this is a no-op). 34 | 35 | Inputs: 36 | 37 | - `keyfile`: The file or pattern of files to use for creating the lookup key of the cache. Due to the nature of `node_modules` potentially having their own `yarn.lock` file, this snippet explicitly excludes that pattern to ensure there is a consistent lookup key before and after package restoration. 38 | - `targetfolder`: The file/folder or pattern of files/folders that you want to cache. The matching files/folders will be represented as the universal package that is uploaded to your Azure DevOps artifact feed. 39 | - `vstsFeed`: The guid representing the artifact feed in Azure DevOps meant to store the build's caches. 40 | 41 | If you do not want to add two build steps to your build definition, you can also use a single task that implicitly adds the `SaveCache` task at the end of the build. For example: 42 | 43 | ```yaml 44 | - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreAndSaveCacheV1.RestoreAndSaveCache@1 45 | inputs: 46 | keyfile: "**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" 47 | targetfolder: "**/node_modules, !**/node_modules/**/node_modules" 48 | vstsFeed: "$(ArtifactFeed)" 49 | 50 | - script: | 51 | yarn install 52 | displayName: Install Dependencies 53 | ``` 54 | 55 | ## Optimistic cache restoration 56 | 57 | If a cache was restored successfully, the build variable `CacheRestored` is set to `true`. This can provide a further performance boost by optionally skipping package install commands entirely. 58 | 59 | In the following example, the 'yarn' task will only run if there was not a cache hit. Although this can provide faster builds, it may not be suitable for production builds. 60 | 61 | ```yaml 62 | - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreAndSaveCacheV1.RestoreAndSaveCache@1 63 | inputs: 64 | keyfile: "**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock" 65 | targetfolder: "**/node_modules, !**/node_modules/**/node_modules" 66 | vstsFeed: "$(ArtifactFeed)" 67 | 68 | - script: | 69 | yarn install 70 | displayName: Install Dependencies 71 | condition: ne(variables['CacheRestored'], 'true') 72 | ``` 73 | 74 | ### Cache aliases 75 | 76 | By default, the name of the variable used for optimistic cache restoration defaults to `CacheRestored`. However, this can be problematic in restoring multiple caches in the same build (E.g. caches for build output and for packages). To work around this, you may set an optional task variable to control the naming of the `CacheRestored` variable. 77 | 78 | For example: 79 | 80 | ```yaml 81 | - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreAndSaveCacheV1.RestoreAndSaveCache@1 82 | inputs: 83 | keyfile: "yarn.lock" 84 | targetfolder: "node_modules" 85 | vstsFeed: "$(ArtifactFeed)" 86 | alias: "Packages" 87 | 88 | - script: | 89 | yarn install 90 | displayName: Install Dependencies 91 | condition: ne(variables['CacheRestored-Packages'], 'true') 92 | ``` 93 | 94 | ## Platform independent caches 95 | 96 | By default, cached archives are platform _dependent_ to support the small differences that may occur in packages produced for a specific platform. If you are certain that the cached archive will be platform _independent_, you can set the task variable `platformIndependent` to true and all platforms will restore the same archive. 97 | 98 | For example: 99 | 100 | ```yaml 101 | - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 102 | inputs: 103 | keyfile: keyfile 104 | targetfolder: bin 105 | vstsFeed: $(ArtifactFeed) 106 | platformIndependent: true 107 | ``` 108 | 109 | ## Onboarding 110 | 111 | 1. Install the extension from the [marketplace](https://marketplace.visualstudio.com/items?itemName=1ESLighthouseEng.PipelineArtifactCaching) into your Azure DevOps organization. 112 | 2. Ensure [Azure Artifacts](https://azure.microsoft.com/en-us/services/devops/artifacts/) is enabled for your organization. 113 | 3. Create a new Azure Artifacts feed to store caches in. After creating the feed, the GUID will be referenced in your build definition. In the examples above, `ArtifactFeed` is a build variable equal to the GUID of the Azure Artifact feed. 114 | 115 | _Note:_ The GUID for your Azure Artifact feed can be found either by using the Azure DevOps [rest apis](https://docs.microsoft.com/en-us/rest/api/azure/devops/artifacts/feed%20%20management/get%20feeds?view=azure-devops-rest-5.0#response) or by creating a build task in the traditional visual designer that references the feed and then selecting "View YAML". 116 | 117 | ## Known limitations 118 | 119 | The task is designed to only cache artifacts that are produced within the build's root directory. This works best for packages that follow this convention (e.g. NPM and NuGet), but not for artifacts that are produced outside of the repo's directory (e.g. Maven). 120 | 121 | The task skips restoring and saving caches on forked repositories by design. This is a security measure to protect cached artifacts from forked sources and a limitation from the Azure Artifacts permissions model (users of forked repositories don't have access to download these artifacts). 122 | 123 | The task is only available to be used within Azure DevOps Services because Azure DevOps Server does not support Universal Packages. 124 | 125 | ## How to build 126 | 127 | ### Prerequisites: Node and NPM 128 | 129 | **Windows and Mac OSX**: Download and install node from [nodejs.org](http://nodejs.org/) 130 | 131 | **Linux**: Install [using package manager](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager) 132 | 133 | From a terminal ensure at least node 4.2 and npm 5: 134 | 135 | ```bash 136 | $ node -v && npm -v 137 | v4.2.0 138 | 5.6.0 139 | ``` 140 | 141 | To install npm separately: 142 | 143 | ``` 144 | [sudo] npm install npm@5 -g 145 | npm -v 146 | 5.6.0 147 | ``` 148 | 149 | Note: On Windows, if it's still returning npm 2.x, run `where npm`. Notice hits in program files. Rename those two npm files and the 5.6.0 in AppData will win. 150 | 151 | ### Install Dependencies 152 | 153 | Once: 154 | 155 | ```bash 156 | npm install 157 | ``` 158 | 159 | ### Build 160 | 161 | The following instructions demonstrate how to build and test either all or a specific task. The output will be sent to 162 | the `_build` directory. You can then use the tfx client to upload this to your server for testing. 163 | 164 | The build will also generate a `task.loc.json` and an english strings file under `Strings` in your source tree. You should check these back in. Another localization process will create the other strings files. 165 | 166 | To build all tasks: 167 | 168 | ```bash 169 | npm run build 170 | ``` 171 | 172 | Optionally, you can build a specific task: 173 | 174 | ```bash 175 | node make.js build --task RestoreCacheV1 176 | ``` 177 | 178 | ## Contributing 179 | 180 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 181 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 182 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 183 | 184 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 185 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 186 | provided by the bot. You will only need to do this once across all repos using our CLA. 187 | 188 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 189 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 190 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 191 | --------------------------------------------------------------------------------