Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 65 additions & 14 deletions BuildTasks/Common/Common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as trl from "vsts-task-lib/toolrunner";
import * as uri from "urijs";

import ToolRunner = trl.ToolRunner;
import * as uuidv5 from "uuidv5";

function writeBuildTempFile(taskName: string, data: any): string {
let tempFile: string;
Expand Down Expand Up @@ -444,6 +445,31 @@ async function getTasksManifestPaths(manifestFile?: string): Promise<string[]> {
return result;
}

async function updateTaskId(manifestFilePath: string, ns: { publisher: string, extensionId: string }): Promise<void> {
tl.debug(`Reading task manifest file: ${manifestFilePath}`);
return Q.nfcall(fs.readFile, manifestFilePath, "utf8").then((data: string) => {
let manifestJSON;
try {
// BOM check
data = data.replace(/^\uFEFF/, (x) => {
tl.warning(`Removing Unicode BOM from manifest file: ${manifestFilePath}.`);
return "";
});
manifestJSON = JSON.parse(data);
}
catch (jsonError) {
throw new Error(`Error parsing task manifest: ${manifestFilePath} - ${jsonError}`);
}

let extensionNs = uuidv5("url", "https://marketplace.visualstudio.com/vsts", true);
manifestJSON.id = uuidv5(extensionNs, `${ns.publisher}.${ns.extensionId}.${manifestJSON.name}`, false);
const newContent = JSON.stringify(manifestJSON, null, "\t");
return Q.nfcall(fs.writeFile, manifestFilePath, newContent).then(() => {
tl.debug(`Task manifest ${manifestFilePath} id updated to ${manifestJSON.id}`);
});
});
}

async function updateTaskVersion(manifestFilePath: string, version: { major: number, minor: number, patch: number }, replacementType: string): Promise<void> {
tl.debug(`Reading task manifest file: ${manifestFilePath}`);
return Q.nfcall(fs.readFile, manifestFilePath, "utf8").then((data: string) => {
Expand Down Expand Up @@ -491,17 +517,18 @@ async function updateTaskVersion(manifestFilePath: string, version: { major: num
* in the extension.
*
*/
export async function checkUpdateTasksVersion(manifestFile?: string): Promise<any> {
export async function checkUpdateTasksManifests(manifestFile?: string): Promise<any> {
// Check if we need to touch in tasks manifest before packaging
const updateTasksVersion = tl.getBoolInput("updateTasksVersion", false);
const updateTasksId = tl.getBoolInput("updateTasksId", false);
let versionReplacementType = tl.getInput("updateTasksVersionType", false);
if (!versionReplacementType || versionReplacementType.length === 0) {
versionReplacementType = "majorminorpatch";
}

let updateTasksFinished = Q.defer();

if (updateTasksVersion) {
if (updateTasksVersion || updateTasksId) {
// Extract the extension version
let extensionVersion;
try {
Expand All @@ -512,14 +539,7 @@ export async function checkUpdateTasksVersion(manifestFile?: string): Promise<an
}

// If extension version specified, let's search for build tasks
if (extensionVersion) {
// Extract version parts Major, Minor, Patch
const versionParts = extensionVersion.split(".");
if (versionParts.length > 3) {
tl.warning(
"Detected a version that consists of more than 3 parts. Build tasks support only 3 parts, ignoring the rest.");
}

if (extensionVersion || updateTasksId) {
try {
const taskManifests: string[] = await getTasksManifestPaths(manifestFile);

Expand All @@ -529,13 +549,44 @@ export async function checkUpdateTasksVersion(manifestFile?: string): Promise<an
return updateTasksFinished.promise;
}

const taskVersion = { major: +versionParts[0], minor: +versionParts[1], patch: +versionParts[2] };
let taskUpdates = [];
if (extensionVersion) {
// Extract version parts Major, Minor, Patch
const versionParts = extensionVersion.split(".");
if (versionParts.length > 3) {
tl.warning("Detected a version that consists of more than 3 parts. Build tasks support only 3 parts, ignoring the rest.");
}

tl.debug(`Processing the following task manifest ${taskManifests}`);
const taskUpdates =
taskManifests.map(manifest => updateTaskVersion(manifest, taskVersion, versionReplacementType));
const taskVersion = { major: +versionParts[0], minor: +versionParts[1], patch: +versionParts[2] };

tl.debug(`Processing the following task manifest ${taskManifests}`);
taskUpdates = taskUpdates.concat(taskManifests.map(manifest => updateTaskVersion(manifest, taskVersion, versionReplacementType)));
}

if (updateTasksId) {
const publisher = tl.getInput("publisherId", true);
let extensionId = tl.getInput("extensionId", true);
const extensionTag = tl.getInput("extensionTag", false);

if (extensionId && extensionTag) {
extensionId += extensionTag;
tl.debug(`Overriding extension id to: ${extensionId}`);
}

if (!(publisher && extensionId)) {
const err = "Currently only supported when 'Publisher' and 'Extension Id' are specified.";
tl.setResult(tl.TaskResult.Failed, `${err}`);
throw err;
}

const ns = { publisher: publisher, extensionId: extensionId };

tl.debug(`Processing the following task manifest ${taskManifests}`);
taskUpdates = taskUpdates.concat(taskManifests.map(manifest => updateTaskId(manifest, ns)));
}

await taskUpdates.forEach(() => updateTasksFinished.resolve(null));

} catch (err) {
updateTasksFinished.reject(`Error determining tasks manifest paths: ${err}`);
}
Expand Down
124 changes: 62 additions & 62 deletions BuildTasks/ExtensionVersion/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,82 +67,82 @@
"visibleRule": "connectTo=TFS"
},
{
"name": "publisherId",
"type": "string",
"label": "Publisher ID",
"defaultValue": "",
"required": true,
"helpMarkDown": "Publisher ID of the extension to be installed.",
"groupName": "extension"
"name": "publisherId",
"type": "string",
"label": "Publisher ID",
"defaultValue": "",
"required": true,
"helpMarkDown": "Publisher ID of the extension to be installed.",
"groupName": "extension"
},
{
"name": "extensionId",
"type": "string",
"label": "Extension ID",
"defaultValue": "",
"helpMarkDown": "Extension ID of the extension to be installed",
"required": true,
"groupName": "extension"
"name": "extensionId",
"type": "string",
"label": "Extension ID",
"defaultValue": "",
"helpMarkDown": "Extension ID of the extension to be installed",
"required": true,
"groupName": "extension"
},
{
"name": "extensionTag",
"type": "string",
"label": "Extension Tag",
"defaultValue": "",
"helpMarkDown": "Extension Tag to append to the extension ID",
"required": false,
"groupName": "extension"
"name": "extensionTag",
"type": "string",
"label": "Extension Tag",
"defaultValue": "",
"helpMarkDown": "Extension Tag to append to the extension ID",
"required": false,
"groupName": "extension"
},
{
"defaultValue": "None",
"helpMarkdown": "Increase a part of the version.",
"label": "Increase version",
"name": "versionAction",
"required": true,
"options": {
"None": "None",
"Patch": "Patch",
"Minor": "Minor",
"Major": "Major"
},
"type": "pickList",
"groupName": "version"
"defaultValue": "None",
"helpMarkdown": "Increase a part of the version.",
"label": "Increase version",
"name": "versionAction",
"required": true,
"options": {
"None": "None",
"Patch": "Patch",
"Minor": "Minor",
"Major": "Major"
},
"type": "pickList",
"groupName": "version"
},
{
"name": "outputVariable",
"type": "string",
"label": "Output Variable",
"defaultValue": "Extension.Version",
"required": true,
"helpMarkDown": "The variable name to assign the version to.",
"groupName": "version"
"name": "outputVariable",
"type": "string",
"label": "Output Variable",
"defaultValue": "Extension.Version",
"required": true,
"helpMarkDown": "The variable name to assign the version to.",
"groupName": "version"
},
{
"name": "extensionVersionOverride",
"type": "string",
"label": "Override Variable",
"defaultValue": "Extension.VersionOverride",
"helpMarkDown": "When this value is specified the extension version task will take it regardless of the version returned from the marketplace. You can use this variable at Queue time to move to the next major version. When the variable value is empty or when the variable doesn't exist the Marketplace will be queried.",
"required": false,
"groupName": "version"
"name": "extensionVersionOverride",
"type": "string",
"label": "Override Variable",
"defaultValue": "Extension.VersionOverride",
"helpMarkDown": "When this value is specified the extension version task will take it regardless of the version returned from the marketplace. You can use this variable at Queue time to move to the next major version. When the variable value is empty or when the variable doesn't exist the Marketplace will be queried.",
"required": false,
"groupName": "version"
},
{
"name": "arguments",
"type": "string",
"label": "Arguments",
"defaultValue": "",
"helpMarkDown": "Additional arguments passed to the package and publishing tool.",
"required": false,
"groupName": "advanced"
"name": "arguments",
"type": "string",
"label": "Arguments",
"defaultValue": "",
"helpMarkDown": "Additional arguments passed to the package and publishing tool.",
"required": false,
"groupName": "advanced"
},
{
"name": "cwd",
"type": "filePath",
"label": "Working Directory",
"defaultValue": "",
"required": false,
"helpMarkDown": "Working directory to run the package and publishing process from. Defaults to the folder where the manifest is located.",
"groupName": "advanced"
"name": "cwd",
"type": "filePath",
"label": "Working Directory",
"defaultValue": "",
"required": false,
"helpMarkDown": "Working directory to run the package and publishing process from. Defaults to the folder where the manifest is located.",
"groupName": "advanced"
}
],
"execution": {
Expand Down
7 changes: 5 additions & 2 deletions BuildTasks/InstallExtension/InstallExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ common.runTfx(tfx => {
tfx.arg(["--accounts"].concat(accounts).map((value, index) => { return value.trim(); }));

const result = tfx.execSync(<any>{ silent: false, failOnStdErr: false });
if (result.stderr.search("error: Error: (?=.*TF1590010)") >= 0) {
tl.warning("Task already installed in target account - Ignoring error TF1590010 returned by tfx.");
tl.setResult(tl.TaskResult.Succeeded, "Ignored");
}

if (result.stderr.search("error: Error: (?!.*TF1590010)") >= 0) {
tl.setResult(tl.TaskResult.Failed, "Failed");
} else {
tl.setResult(tl.TaskResult.Succeeded, "Succeeded");
}
});
2 changes: 1 addition & 1 deletion BuildTasks/PackageExtension/PackageExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ async function run() {
tfx.argIf(outputPath, ["--output-path", outputPath]);

// Before executing check update on tasks version
await common.checkUpdateTasksVersion();
await common.checkUpdateTasksManifests();
const outputStream = new common.TfxJsonOutputStream(false);

tfx.exec(<any>{ outStream: outputStream, failOnStdErr: true }).then(code => {
Expand Down
9 changes: 9 additions & 0 deletions BuildTasks/PackageExtension/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@
"visibleRule": "updateTasksVersion=true",
"groupName": "overrides"
},
{
"name": "updateTasksId",
"type": "boolean",
"label": "Override task id",
"defaultValue": "false",
"required": false,
"helpMarkDown": "EXPERIMENTAL - Search for contributed tasks in extension manifests and updates the id specified in each Build and Release task found based on the Publisher, ExtensionId and TaskName.",
"groupName": "overrides"
},
{
"name": "extensionVisibility",
"type": "pickList",
Expand Down
10 changes: 7 additions & 3 deletions BuildTasks/PublishExtension/PublishExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ common.runTfx(tfx => {
cleanupTfxArgs = common.validateAndSetTfxManifestArguments(tfx);

// Update tasks version if needed
runBeforeTfx = runBeforeTfx.then(() => common.checkUpdateTasksVersion());
runBeforeTfx = runBeforeTfx.then(() => common.checkUpdateTasksManifests());
} else {
// Set vsix file argument
let vsixFilePattern = tl.getPathInput("vsixFile", true);
Expand Down Expand Up @@ -61,7 +61,7 @@ common.runTfx(tfx => {
const extensionVisibility = tl.getInput("extensionVisibility", false) || "";
const extensionPricing = tl.getInput("extensionPricing", false);
const extensionVersion = common.getExtensionVersion();

const updateTasksId = tl.getBoolInput("updateTasksId", false);
const updateTasksVersion = tl.getBoolInput("updateTasksVersion", false);

if (publisher
Expand All @@ -70,7 +70,8 @@ common.runTfx(tfx => {
|| extensionName
|| (extensionPricing && extensionPricing !== "default")
|| (extensionVisibility && extensionVisibility !== "default")
|| extensionVersion) {
|| extensionVersion
|| updateTasksId ) {

tl.debug("Start editing of VSIX");
let ve = new vsixeditor.VSIXEditor(vsixFile, vsixOutput);
Expand All @@ -86,6 +87,9 @@ common.runTfx(tfx => {
ve.editVersion(extensionVersion);
ve.editUpdateTasksVersion(updateTasksVersion);
}
if (updateTasksId) {
ve.editUpdateTasksId(updateTasksId);
}

runBeforeTfx = runBeforeTfx.then(() => ve.endEdit().then(vsixGeneratedFile => {
tfx.arg(["--vsix", vsixGeneratedFile]);
Expand Down
Loading