Skip to content

Overwrite featureId during packaging #909

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
46 changes: 44 additions & 2 deletions BuildTasks/Common/v5/Common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AzureRMEndpoint } from "azure-pipelines-tasks-azure-arm-rest/azure-arm-
import fse from "fs-extra";
import uuidv5 from "uuidv5";
import tmp from "tmp";
import { forEach } from "core-js/core/array";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this?


function writeBuildTempFile(taskName: string, data: any): string {
const tempDir = tl.getVariable("Agent.TempDirectory");
Expand Down Expand Up @@ -378,6 +379,35 @@ function updateExtensionManifestTaskIds(manifest: any, originalTaskId: string, n
return manifest;
}

function updateExtensionManifestsFeatureConstrains(manifest: any): unknown {
Copy link
Preview

Copilot AI Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function name 'updateExtensionManifestsFeatureConstrains' appears to be misspelled. Consider renaming it to 'updateExtensionManifestsFeatureConstraints' for clarity.

Suggested change
function updateExtensionManifestsFeatureConstrains(manifest: any): unknown {
function updateExtensionManifestsFeatureConstraints(manifest: any): unknown {

Copilot uses AI. Check for mistakes.

if (!manifest.contributions) {
tl.debug(`No contributions found`);
return manifest;
}

const extensionTag = tl.getInput("extensionTag", false) || "";
const extensionId = `${(tl.getInput("extensionId", false) || manifest.id)}${extensionTag}`;

manifest.contributions
.filter((c: any) => c.constraints && c.constraints.length > 0)
.forEach((c: any) => {
c.constraints.forEach((constraint: any) => {
if (constraint.properties?.featureId) {
tl.debug(`Extension manifest featureId before: ${constraint.properties.featureId}`);

const parts = constraint.properties.featureId.split(".");
parts[1] = extensionId;
const newFeatureId = parts.join(".");
constraint.properties.featureId = newFeatureId;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any cases where people opt into someone else's feature Id? Say, the Microsoft new Boards Hub feature?

I haven't personally used these IDs, so there might be cases that we must keep in mind.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I suspect the publisher may also be overwritten here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like ideally we'd loop over the feature contributions and then only overwrite the ones that match the same name at least.

https://github.com/gustavobergamim/azdevops-pipeline-approval/blob/master/vss-extension.json#L62-L99


tl.debug(`Extension manifest featureId after: ${newFeatureId}`);
}
});
});

return manifest;
}

function updateTaskVersion(manifest: any, extensionVersionString: string, extensionVersionType: string): unknown {
const versionParts = extensionVersionString.split(".");
if (versionParts.length > 3) {
Expand Down Expand Up @@ -414,6 +444,8 @@ function updateTaskVersion(manifest: any, extensionVersionString: string, extens
export async function updateManifests(manifestPaths: string[]): Promise<void> {
const updateTasksVersion = tl.getBoolInput("updateTasksVersion", false);
const updateTasksId = tl.getBoolInput("updateTasksId", false);
const extensionTag = tl.getInput("extensionTag", false);
const updateFeatureConstraints = tl.getBoolInput("updateFeatureConstraints", false);

if (updateTasksVersion || updateTasksId) {
if (!(manifestPaths && manifestPaths.length)) {
Expand All @@ -423,7 +455,17 @@ export async function updateManifests(manifestPaths: string[]): Promise<void> {
tl.debug(`Found manifests: ${manifestPaths.join(", ")}`);

const tasksIds = await updateTaskManifests(manifestPaths, updateTasksId, updateTasksVersion);
await updateExtensionManifests(manifestPaths, tasksIds);
await updateExtensionManifestsTaskIds(manifestPaths, tasksIds);
}

if (extensionTag && updateFeatureConstraints) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if extensionId || extentionTag || publisherId

if (!(manifestPaths && manifestPaths.length)) {
manifestPaths = getExtensionManifestPaths();
}

tl.debug(`Found manifests: ${manifestPaths.join(", ")}`);

await updateExtensionManifestsFeatureConstrains(manifestPaths);
}
Comment on lines +468 to 469
Copy link
Preview

Copilot AI Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function 'updateExtensionManifestsFeatureConstrains' is being passed 'manifestPaths' (an array) whereas it expects a manifest object. Consider iterating over 'manifestPaths' to load each manifest before processing.

Suggested change
await updateExtensionManifestsFeatureConstrains(manifestPaths);
}
for (const manifestPath of manifestPaths) {
const manifest = await getManifest(manifestPath);
await updateExtensionManifestsFeatureConstrains(manifest);
}

Copilot uses AI. Check for mistakes.

}

Expand Down Expand Up @@ -475,7 +517,7 @@ async function updateTaskManifests(manifestPaths: string[], updateTasksId: boole
return tasksIds;
}

async function updateExtensionManifests(manifestPaths: string[], updatedTaskIds: [string, string][]): Promise<void> {
async function updateExtensionManifestsTaskIds(manifestPaths: string[], updatedTaskIds: [string, string][]): Promise<void> {
await Promise.all(manifestPaths.map(async (path) => {
tl.debug(`Patching: ${path}.`);
let originalManifest = await getManifest(path);
Expand Down
5 changes: 4 additions & 1 deletion BuildTasks/PublishExtension/v5/PublishExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ await common.runTfx(async tfx => {
const extensionVersion = common.getExtensionVersion();
const updateTasksId = tl.getBoolInput("updateTasksId", false);
const updateTasksVersion = tl.getBoolInput("updateTasksVersion", false);
const updateFeatureConstraints = tl.getBoolInput("updateFeatureConstraints", false);

if (publisher
|| extensionId
Expand All @@ -67,7 +68,8 @@ await common.runTfx(async tfx => {
|| (extensionPricing && extensionPricing !== "default")
|| (extensionVisibility && extensionVisibility !== "default")
|| extensionVersion
|| updateTasksId) {
|| updateTasksId
|| updateFeatureConstraints) {

tl.debug("Start editing of VSIX");
const ve = new VsixEditor(vsixFile, vsixOutput);
Expand All @@ -77,6 +79,7 @@ await common.runTfx(async tfx => {
if (extensionId) { ve.editId(extensionId); }
if (extensionTag) { ve.editIdTag(extensionTag); }
if (extensionName) { ve.editExtensionName(extensionName); }
if (updateFeatureConstraints) { ve.editUpdateFeatureConstraints(updateFeatureConstraints); }
if (extensionVisibility) { ve.editExtensionVisibility(extensionVisibility); }
if (extensionPricing) { ve.editExtensionPricing(extensionPricing); }
if (extensionVersion) {
Expand Down
9 changes: 9 additions & 0 deletions BuildTasks/PublishExtension/v5/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,15 @@
"required": false,
"groupName": "manifest"
},
{
"name": "updateFeatureConstraints",
"type": "boolean",
"label": "Override feature Id",
"defaultValue": "false",
"required": false,
"helpMarkDown": "Search for feature constraints in extension manifests and updates the featureId to match the actual extension id.",
"groupName": "manifest"
},
{
"name": "updateTasksVersion",
"type": "boolean",
Expand Down
10 changes: 9 additions & 1 deletion BuildTasks/PublishExtension/v5/vsixeditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export default class VSIXEditor {
private extensionPricing: string = null;
private updateTasksVersion = true;
private updateTasksId = true;
private updateFeatureConstraints = false;

constructor(
public inputFile: string,
Expand Down Expand Up @@ -203,7 +204,9 @@ export default class VSIXEditor {
tl.debug("Extracting files to " + dirPath);

await this.extractArchive(this.inputFile, dirPath);
if (this.versionNumber && this.updateTasksVersion || this.updateTasksId) {

if (this.versionNumber && this.updateTasksVersion || this.updateTasksId ||
this.editIdTag && this.editUpdateFeatureConstraints) {
Copy link
Preview

Copilot AI Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditional block references 'this.editIdTag && this.editUpdateFeatureConstraints' which appears to be a mistake since there is no 'editUpdateFeatureConstraints' property; it is likely intended to use 'this.updateFeatureConstraints'.

Suggested change
this.editIdTag && this.editUpdateFeatureConstraints) {
this.editIdTag && this.updateFeatureConstraints) {

Copilot uses AI. Check for mistakes.

tl.debug("Look for build tasks manifest");
const extensionManifest = path.join(dirPath, "extension.vsomanifest");
await common.checkUpdateTasksManifests(extensionManifest);
Expand Down Expand Up @@ -344,6 +347,11 @@ export default class VSIXEditor {
this.updateTasksId = updateTasksId;
}

public editUpdateFeatureConstraints(updateFeatureConstraints: boolean) {
this.validateEditMode();
this.updateFeatureConstraints = updateFeatureConstraints;
}

private validateEditMode() : void {
if (!this.edit) { throw new Error("Editing is not started"); }
}
Expand Down