Custom ADO pipeline task for AVD AppAttach (#602)

* Update CODEOWNERS

Adding  @microsoft/MSIX-ENG-IDC to pipelines-task project

* Add custom pipeline task for MSIX AVD AppAttach

* Refer AppAttachFramework through nupkg and added telemetry

* Updated readme

* changes for Review comments and other UX changes

* pascal case nomenclature for ps1 files. publishAVD.ps1

* Address review comments for AppAttachFramework source details

* install AppAttach framework on building dependencies

* Review comments Incorporation

* Review comments incorporation

* Review comments incorporation for separate nuget install

* better handling for storage account id parsing

* Update AppAttachFramework nupkg and remove node-fetch dependencies accross project as no more required

* Add more code owners for misx-packaging repo

* Use winget install only for local installation of nuget as production build already contains nuget installed

* git ignore appattachFramework installed artifacts(DLLs)
This commit is contained in:
npuvvada 2023-12-27 10:58:23 +05:30 коммит произвёл GitHub
Родитель eb3add86ec
Коммит b5ab4138fa
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
31 изменённых файлов: 1956 добавлений и 21 удалений

Просмотреть файл

@ -1,5 +1,5 @@
# MSIX SDK
* @msftrubengu @ranm-msft @johnmcpms
* @msftrubengu @ranm-msft @johnmcpms @sachinkumar-2106 @kyelamarthi1
# MsixCoreInstaller owners
/MsixCore/ @wcheng-msft @jyvenugo @sarjanas-msft @microsoft/MSIX-ENG-IDC
@ -8,4 +8,4 @@
/tools/utils/ @ranm-msft @msftrubengu @johnmcpms @florelis
# Azure Pipelines tasks
/tools/pipelines-tasks @ranm-msft @msftrubengu @johnmcpms @florelis @microsoft/MSIX-ENG-IDC
/tools/pipelines-tasks @microsoft/MSIX-ENG-IDC

6
tools/pipelines-tasks/.gitignore поставляемый
Просмотреть файл

@ -28,4 +28,8 @@ pat.txt
ps_modules/
# Don't exclude VS Code configuration for this project as it contains debug configuration
!.vscode/
!.vscode/
# Don't exclude AppAttach Framework artifacts
!common/lib/*.nupkg
common/lib/AppAttachFrameworkDLL

Просмотреть файл

@ -0,0 +1,27 @@
<#
.SYNOPSIS
This script invokes AppAttachFramwork DLL to perform App Attach
.DESCRIPTION
This script triggers AppAttach framework DLL with required input json configuration to porform App Attach.
.PARAMETER inputJsonStr
Specifies input json configuration to trigger AppAttach Framework DLL with for AppAttach.
.PARAMETER targetDLL
Specifies the target DLL to be triggered for AppAttach.
#>
param([string]$inputJsonStr, [string]$targetDLL)
Add-Type -Path $targetDLL
# Create and invoke AppAttachKernel object with configured json
$custObj = New-Object AppAttachKernel.AppAttachKernelController($inputJsonStr)
$token = New-Object 'System.Threading.CancellationToken'
$appAttachFlowResponse = $custObj.execute($token)
if ($appAttachFlowResponse.isError)
{
throw (($appAttachFlowResponse.getAppAttachOutputs())[0]).getMessage()
}

Просмотреть файл

@ -0,0 +1,40 @@
<#
.SYNOPSIS
This script Reports Exceptions to AppAttach Telemetry
.DESCRIPTION
This script triggers AppAttachTelemetry DLL with required parameters to capture exceptions in AppAttach Telemetry
.PARAMETER exceptionMessage
Specifies the exception message to be captured
.PARAMETER targetDLL
Specifies the target DLL to be triggered for AppAttach Telemetry.
#>
param([string]$exceptionMessage, [string]$targetDLL, [string]$clientType, [string]$clientVersion)
try
{
Add-Type -Path $targetDLL
# Create and invoke AppAttachTelemetry object.
$telemetryProvider = [AppAttachTelemetry.TelemetryProvider]::s_instance
$appAttachExceptionBuilderObj = New-Object AppAttachTelemetry.Builders.AppAttachExceptionsBuilder
$appAttachExceptionBuilderObj.SetIsSuccessful("False") | Out-Null
$appAttachExceptionBuilderObj.SetErrorCode($exceptionMessage) | Out-Null
$appAttachExceptionBuilderObj.SetErrorDesc($exceptionMessage) | Out-Null
$appAttachExceptionBuilderObj.SetClientType($clientType) | Out-Null
$appAttachExceptionBuilderObj.SetClientVersion($clientVersion) | Out-Null
$appAttachExceptionBuilderObj.SetState("ErrorOccurred") | Out-Null
$appAttachExceptions = $appAttachExceptionBuilderObj.Build()
$telemetryProvider.ReportException($appAttachExceptions)
}
catch
{
Write-Error "Error Occurred:"
Write-Error $_
}

Просмотреть файл

@ -0,0 +1,22 @@
{
"loc.friendlyName": "Publish MSIX app attach package to AVD",
"loc.helpMarkDown": "",
"loc.description": "Publish MSIX package to Azure Virtual Desktop",
"loc.instanceNameFormat": "Publish MSIX app attach package to AVD",
"loc.input.label.vhdxPath": "VHDX Path",
"loc.input.help.vhdxPath": "Path to VHDX Package path.",
"loc.input.label.connectedServiceNameARM": "Azure subscription",
"loc.input.help.connectedServiceNameARM": "Azure Resource Manager subscription for the deployment.",
"loc.input.label.resourceGroupName": "Resource group",
"loc.input.help.resourceGroupName": "Azure Resource group containing AVD resources for app attach",
"loc.input.label.storageAccount": "Storage account",
"loc.input.help.storageAccount": "Azure Storage Account containing target Azure File Share for app attach",
"loc.input.label.fileShare": "File share",
"loc.input.help.fileShare": "Azure File Share to upload app attach artifact and used for app attach",
"loc.input.label.hostPool": "Host pool",
"loc.input.help.hostPool": "Azure Host Pool for app attach",
"loc.input.label.workSpace": "Workspace",
"loc.input.help.workSpace": "AVD Work Space for app attach",
"loc.input.label.applicationGroup": "Application group",
"loc.input.help.applicationGroup": "AVD Application Group for app attach"
}

Двоичные данные
tools/pipelines-tasks/AVDAppAttachPublish/icon.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Просмотреть файл

@ -0,0 +1,146 @@
import tl = require('azure-pipelines-task-lib/task');
import { ToolRunner } from 'azure-pipelines-task-lib/toolrunner';
import path = require('path');
import armStorage = require('azure-pipelines-tasks-azure-arm-rest/azure-arm-storage');
import { AzureRMEndpoint } from 'azure-pipelines-tasks-azure-arm-rest/azure-arm-endpoint';
import { AzureEndpoint, StorageAccount } from 'azure-pipelines-tasks-azure-arm-rest/azureModels';
import * as telemetry from "azure-pipelines-tasks-utility-common/telemetry";
import * as os from 'os';
import * as url from 'url';
import * as fs from 'fs';
import helpers = require('common-helpers/helpers');
const HELPER_SCRIPT = path.join(__dirname, 'PublishAVD.ps1');
const HELPER_SCRIPT_EXCEPTIONS_TELEMETRY = path.join(__dirname, 'ReportExceptionTelemetry.ps1');
const TARGET_DLL = path.join(__dirname,'node_modules/common-helpers/lib/AppAttachFrameworkDLL/AppAttachKernel.dll');
const TARGET_DLL_TELEMETRY = path.join(__dirname, 'node_modules/common-helpers/lib/AppAttachFrameworkDLL/AppAttachTelemetry.dll');
function isNonEmpty(str: string): boolean {
return (!!str && !!str.trim());
}
function getResourceGroupNameFromUri(resourceUri: string): string {
if (isNonEmpty(resourceUri)) {
const parsedUrl = url.parse(resourceUri, true);
const pathname = parsedUrl.pathname || '';
const segments = pathname.split('/');
const resourceGroupIndex = segments.indexOf('resourceGroups');
if (resourceGroupIndex !== -1 && resourceGroupIndex < segments.length - 1) {
return segments[resourceGroupIndex + 1];
}
}
throw "Invalid ResourceUri";
}
async function run(): Promise<void> {
let isSuccessful = true;
let exceptionMessage: string = "";
try {
let connectedServiceName = tl.getInput('connectedServiceNameARM', true)!;
let subscriptionID: string = tl.getEndpointDataParameter(connectedServiceName, 'subscriptionid', true)!;
let storageAccountName = tl.getInput('storageAccount', true)!;
let azureEndpoint: AzureEndpoint = await new AzureRMEndpoint(connectedServiceName).getEndpoint();
let accessToken: string = (await azureEndpoint.applicationTokenCredentials.getToken()).toString();
const storageArmClient = new armStorage.StorageManagementClient(azureEndpoint.applicationTokenCredentials, (azureEndpoint.subscriptionID)!);
let storageAccount: StorageAccount = await storageArmClient.storageAccounts.get(storageAccountName);
let storageAccountResourceGroupName = getResourceGroupNameFromUri(storageAccount.id);
let accessKeys = await storageArmClient.storageAccounts.listKeys(storageAccountResourceGroupName, storageAccountName, null);
let accessKey: string = accessKeys[0];
let storageAccountConnectionString: string = `DefaultEndpointsProtocol=https;AccountName=${storageAccountName};AccountKey=${accessKey};EndpointSuffix=core.windows.net`;
let username: string = (process.env.BUILD_REQUESTEDFOREMAIL)!;
const PUBLISH_TO_AZURE_TASK = '8';
const appattachConfig = {
'emailId': username,
'accessToken': accessToken,
'azureStorageKey': storageAccountConnectionString,
'azureWorkspace': tl.getInput('workSpace', true),
'azureStorageFileShare': tl.getInput('fileShare', true),
'azureStorageAccount': storageAccountName,
'azureSubscriptionId': subscriptionID,
'azureResourceGroup': tl.getInput('resourceGroupName', true),
'azureHostPoolName': tl.getInput('hostPool', true),
'azureApplicationGroupName': tl.getInput('applicationGroup', true),
'taskType': PUBLISH_TO_AZURE_TASK,
'appAttachImagePath': tl.getInput('vhdxPath', true),
'clientType': helpers.CLIENT_TYPE,
'clientVersion': helpers.CLIENT_VERSION
};
const jsonString = JSON.stringify(appattachConfig);
appattachConfig['accessToken'] = "***"; // mask access token while displaying in log
appattachConfig['azureStorageKey'] = "***"; // mask storage account key while displaying in log
tl.debug(JSON.stringify(appattachConfig));
const powershellRunner: ToolRunner = helpers.getPowershellRunner(HELPER_SCRIPT);
powershellRunner.arg(['-inputJsonStr', '\'' + jsonString + '\'']);
powershellRunner.arg(['-targetDLL', TARGET_DLL]);
let execResult = await powershellRunner.execSync();
if (execResult.code) {
throw execResult.stderr;
}
} catch (error) {
isSuccessful = false;
exceptionMessage = error as string;
const regexPattern: RegExp = /FullyQualifiedErrorId\s*:\s*([^]+?)\n/;
const match = exceptionMessage.match(regexPattern);
if (match && match[1]) {
exceptionMessage = match[1].trim();
}
console.error(tl.loc("AppAttachPublish Error", exceptionMessage));
const powershellRunner: ToolRunner = helpers.getPowershellRunner(HELPER_SCRIPT_EXCEPTIONS_TELEMETRY);
powershellRunner.arg(['-exceptionMessage', exceptionMessage]);
powershellRunner.arg(['-targetDLL', TARGET_DLL_TELEMETRY]);
powershellRunner.arg(['-clientType', helpers.CLIENT_TYPE]);
powershellRunner.arg(['-clientVersion', helpers.CLIENT_VERSION]);
await powershellRunner.execSync();
throw exceptionMessage;
} finally {
const appAttachLogDir: string = path.join(os.tmpdir(), 'AppAttach');
if (tl.exist(appAttachLogDir)) {
// display log file content to console
const appAttachLogDirFiles = fs.readdirSync(appAttachLogDir);
const appAttachlogFile = path.join(appAttachLogDir, appAttachLogDirFiles[0]);
fs.readFile(appAttachlogFile, 'utf8', (err, data) => {
if (err) {
console.error('Error reading the file:', err);
} else {
console.log(appAttachlogFile, data);
}
});
}
logTelemetry({
Version: helpers.CLIENT_VERSION,
AppAttachImagePath: tl.getInput('vhdxPath', true),
IsSuccessful: isSuccessful,
ExceptionMessage: exceptionMessage
});
}
}
function logTelemetry(params: any) {
try {
telemetry.emitTelemetry("MSIX-Packaging", "AVDAppAttachPublish", params);
} catch (err) {
tl.debug(`Unable to log AVDAppAttachPublish task telemetry. Err:( ${err} )`);
}
}
run().catch(err =>
{
tl.setResult(tl.TaskResult.Failed, err);
})

1061
tools/pipelines-tasks/AVDAppAttachPublish/package-lock.json сгенерированный Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,27 @@
{
"name": "msix-tasks-avd-app-attach-publish",
"version": "1.0.0",
"description": "Task to Publish MSIX application on AVD",
"repository": {
"type": "git",
"url": "git+github.com/microsoft/msix-packaging.git"
},
"scripts": {},
"author": "Microsoft Corporation",
"license": "MIT",
"dependencies": {
"@types/mocha": "^5.2.7",
"@types/node": "^10.17.0",
"@types/q": "1.0.7",
"azure-pipelines-task-lib": "4.3.1",
"azure-pipelines-tasks-azure-arm-rest": "3.217.1",
"azure-pipelines-tasks-utility-common": "^3.230.0",
"common": "^0.2.5",
"common-helpers": "file:../common/msix-tasks-helpers-1.0.0.tgz",
"moment": "^2.29.4",
"uuid": "^8.3.0"
},
"devDependencies": {
"typescript": "4.0.2"
}
}

Просмотреть файл

@ -0,0 +1,142 @@
{
"$schema": "https://raw.githubusercontent.com/Microsoft/azure-pipelines-task-lib/master/tasks.schema.json",
"id": "59de78ad-c5b7-4750-89dd-7bb24a1eb964",
"name": "AVDAppAttachPublish",
"friendlyName": "Publish MSIX app attach package to AVD",
"instanceNameFormat": "Publish MSIX app attach package to AVD",
"description": "Publish MSIX package to Azure Virtual Desktop",
"helpMarkDown": "",
"category": "Deploy",
"author": "Microsoft Corporation",
"version": {
"Major": 1,
"Minor": 0,
"Patch": 0
},
"inputs": [
{
"name": "vhdxPath",
"type": "string",
"label": "VHDX Path",
"defaultValue": "",
"required": true,
"helpMarkDown": "Path to VHDX Package path."
},
{
"name": "connectedServiceNameARM",
"type": "connectedService:AzureRM",
"label": "Azure subscription",
"defaultValue": "",
"required": true,
"helpMarkDown": "Azure Resource Manager subscription for the deployment."
},
{
"name": "resourceGroupName",
"type": "pickList",
"label": "Resource group",
"defaultValue": "",
"required": true,
"properties": {
"EditableOptions": "True"
},
"helpMarkDown": "Azure Resource group containing AVD resources for app attach"
},
{
"name": "storageAccount",
"type": "pickList",
"label": "Storage account",
"defaultValue": "",
"required": true,
"properties": {
"EditableOptions": "True"
},
"helpMarkDown": "Azure Storage Account containing target Azure File Share for app attach"
},
{
"name": "fileShare",
"type": "pickList",
"label": "File share",
"defaultValue": "",
"required": true,
"properties": {
"EditableOptions": "True"
},
"helpMarkDown": "Azure File Share to upload app attach artifact and used for app attach"
},
{
"name": "hostPool",
"type": "pickList",
"label": "Host pool",
"defaultValue": "",
"required": true,
"properties": {
"EditableOptions": "True"
},
"helpMarkDown": "Azure Host Pool for app attach"
},
{
"name": "workSpace",
"type": "pickList",
"label": "Workspace",
"defaultValue": "",
"required": true,
"properties": {
"EditableOptions": "True"
},
"helpMarkDown": "AVD Work Space for app attach"
},
{
"name": "applicationGroup",
"type": "pickList",
"label": "Application group",
"defaultValue": "",
"required": true,
"properties": {
"EditableOptions": "True"
},
"helpMarkDown": "AVD Application Group for app attach"
}
],
"dataSourceBindings": [
{
"target": "resourceGroupName",
"endpointId": "$(connectedServiceNameARM)",
"dataSourceName": "AzureResourceGroups"
},
{
"target": "storageAccount",
"endpointId": "$(connectedServiceNameARM)",
"endpointUrl": "{{endpoint.url}}subscriptions/{{endpoint.subscriptionId}}/resourcegroups/$(resourceGroupName)/providers/Microsoft.Storage/storageAccounts?api-version=2023-01-01",
"resultSelector": "jsonpath:$.value[*].name"
},
{
"target": "fileShare",
"endpointId": "$(connectedServiceNameARM)",
"endpointUrl": "{{endpoint.url}}subscriptions/{{endpoint.subscriptionId}}/resourcegroups/$(resourceGroupName)/providers/Microsoft.Storage/storageAccounts/$(storageAccount)/fileServices/default/shares?api-version=2023-01-01",
"resultSelector": "jsonpath:$.value[*].name"
},
{
"target": "hostPool",
"endpointId": "$(connectedServiceNameARM)",
"endpointUrl": "{{endpoint.url}}subscriptions/{{endpoint.subscriptionId}}/resourcegroups/$(resourceGroupName)/providers/Microsoft.DesktopVirtualization/hostPools?api-version=2022-02-10-preview",
"resultSelector": "jsonpath:$.value[*].name"
},
{
"target": "workSpace",
"endpointId": "$(connectedServiceNameARM)",
"endpointUrl": "{{endpoint.url}}subscriptions/{{endpoint.subscriptionId}}/resourcegroups/$(resourceGroupName)/providers/Microsoft.DesktopVirtualization/workspaces?api-version=2022-02-10-preview",
"resultSelector": "jsonpath:$.value[*].name"
},
{
"target": "applicationGroup",
"endpointId": "$(connectedServiceNameARM)",
"endpointUrl": "{{endpoint.url}}subscriptions/{{endpoint.subscriptionId}}/resourcegroups/$(resourceGroupName)/providers/Microsoft.DesktopVirtualization/applicationGroups?api-version=2022-02-10-preview",
"resultSelector": "jsonpath:$.value[*].name"
}
],
"execution": {
"Node10": {
"target": "main.js"
}
}
}

Просмотреть файл

@ -0,0 +1,142 @@
{
"$schema": "https://raw.githubusercontent.com/Microsoft/azure-pipelines-task-lib/master/tasks.schema.json",
"id": "59de78ad-c5b7-4750-89dd-7bb24a1eb964",
"name": "AVDAppAttachPublish",
"friendlyName": "ms-resource:loc.friendlyName",
"instanceNameFormat": "ms-resource:loc.instanceNameFormat",
"description": "ms-resource:loc.description",
"helpMarkDown": "ms-resource:loc.helpMarkDown",
"category": "Deploy",
"author": "Microsoft Corporation",
"version": {
"Major": 1,
"Minor": 0,
"Patch": 0
},
"inputs": [
{
"name": "vhdxPath",
"type": "string",
"label": "ms-resource:loc.input.label.vhdxPath",
"defaultValue": "",
"required": true,
"helpMarkDown": "ms-resource:loc.input.help.vhdxPath"
},
{
"name": "connectedServiceNameARM",
"type": "connectedService:AzureRM",
"label": "ms-resource:loc.input.label.connectedServiceNameARM",
"defaultValue": "",
"required": true,
"helpMarkDown": "ms-resource:loc.input.help.connectedServiceNameARM"
},
{
"name": "resourceGroupName",
"type": "pickList",
"label": "ms-resource:loc.input.label.resourceGroupName",
"defaultValue": "",
"required": true,
"properties": {
"EditableOptions": "True"
},
"helpMarkDown": "ms-resource:loc.input.help.resourceGroupName"
},
{
"name": "storageAccount",
"type": "pickList",
"label": "ms-resource:loc.input.label.storageAccount",
"defaultValue": "",
"required": true,
"properties": {
"EditableOptions": "True"
},
"helpMarkDown": "ms-resource:loc.input.help.storageAccount"
},
{
"name": "fileShare",
"type": "pickList",
"label": "ms-resource:loc.input.label.fileShare",
"defaultValue": "",
"required": true,
"properties": {
"EditableOptions": "True"
},
"helpMarkDown": "ms-resource:loc.input.help.fileShare"
},
{
"name": "hostPool",
"type": "pickList",
"label": "ms-resource:loc.input.label.hostPool",
"defaultValue": "",
"required": true,
"properties": {
"EditableOptions": "True"
},
"helpMarkDown": "ms-resource:loc.input.help.hostPool"
},
{
"name": "workSpace",
"type": "pickList",
"label": "ms-resource:loc.input.label.workSpace",
"defaultValue": "",
"required": true,
"properties": {
"EditableOptions": "True"
},
"helpMarkDown": "ms-resource:loc.input.help.workSpace"
},
{
"name": "applicationGroup",
"type": "pickList",
"label": "ms-resource:loc.input.label.applicationGroup",
"defaultValue": "",
"required": true,
"properties": {
"EditableOptions": "True"
},
"helpMarkDown": "ms-resource:loc.input.help.applicationGroup"
}
],
"dataSourceBindings": [
{
"target": "resourceGroupName",
"endpointId": "$(connectedServiceNameARM)",
"dataSourceName": "AzureResourceGroups"
},
{
"target": "storageAccount",
"endpointId": "$(connectedServiceNameARM)",
"endpointUrl": "{{endpoint.url}}subscriptions/{{endpoint.subscriptionId}}/resourcegroups/$(resourceGroupName)/providers/Microsoft.Storage/storageAccounts?api-version=2023-01-01",
"resultSelector": "jsonpath:$.value[*].name"
},
{
"target": "fileShare",
"endpointId": "$(connectedServiceNameARM)",
"endpointUrl": "{{endpoint.url}}subscriptions/{{endpoint.subscriptionId}}/resourcegroups/$(resourceGroupName)/providers/Microsoft.Storage/storageAccounts/$(storageAccount)/fileServices/default/shares?api-version=2023-01-01",
"resultSelector": "jsonpath:$.value[*].name"
},
{
"target": "hostPool",
"endpointId": "$(connectedServiceNameARM)",
"endpointUrl": "{{endpoint.url}}subscriptions/{{endpoint.subscriptionId}}/resourcegroups/$(resourceGroupName)/providers/Microsoft.DesktopVirtualization/hostPools?api-version=2022-02-10-preview",
"resultSelector": "jsonpath:$.value[*].name"
},
{
"target": "workSpace",
"endpointId": "$(connectedServiceNameARM)",
"endpointUrl": "{{endpoint.url}}subscriptions/{{endpoint.subscriptionId}}/resourcegroups/$(resourceGroupName)/providers/Microsoft.DesktopVirtualization/workspaces?api-version=2022-02-10-preview",
"resultSelector": "jsonpath:$.value[*].name"
},
{
"target": "applicationGroup",
"endpointId": "$(connectedServiceNameARM)",
"endpointUrl": "{{endpoint.url}}subscriptions/{{endpoint.subscriptionId}}/resourcegroups/$(resourceGroupName)/providers/Microsoft.DesktopVirtualization/applicationGroups?api-version=2022-02-10-preview",
"resultSelector": "jsonpath:$.value[*].name"
}
],
"execution": {
"Node10": {
"target": "main.js"
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
{
"extends": "../tsconfig.json"
}

Просмотреть файл

@ -143,7 +143,7 @@
"name": "msix-tasks-helpers",
"version": "1.0.0",
"resolved": "file:../common/msix-tasks-helpers-1.0.0.tgz",
"integrity": "sha512-PzAGAz2kcID8hnqTR9qHGR52sul+aqpZl8DXzNmO5mu5J+LSuveVkA6LV+efFr7oimU5o2vNdC2AMCLm/505Mg==",
"integrity": "sha512-RJJbqZKd/lSDour+y1c7J6l4BWIcJ1uDBQIXqNzkST2OAoOvJngY134a55PlIzFiQtZ4+lkDi20Qcmq1a/CctA==",
"license": "MIT",
"dependencies": {
"@types/xml2js": "^0.4.8",

Просмотреть файл

@ -143,7 +143,7 @@
"name": "msix-tasks-helpers",
"version": "1.0.0",
"resolved": "file:../common/msix-tasks-helpers-1.0.0.tgz",
"integrity": "sha512-PzAGAz2kcID8hnqTR9qHGR52sul+aqpZl8DXzNmO5mu5J+LSuveVkA6LV+efFr7oimU5o2vNdC2AMCLm/505Mg==",
"integrity": "sha512-RJJbqZKd/lSDour+y1c7J6l4BWIcJ1uDBQIXqNzkST2OAoOvJngY134a55PlIzFiQtZ4+lkDi20Qcmq1a/CctA==",
"license": "MIT",
"dependencies": {
"@types/xml2js": "^0.4.8",

Просмотреть файл

@ -178,7 +178,7 @@
"name": "msix-tasks-helpers",
"version": "1.0.0",
"resolved": "file:../common/msix-tasks-helpers-1.0.0.tgz",
"integrity": "sha512-PzAGAz2kcID8hnqTR9qHGR52sul+aqpZl8DXzNmO5mu5J+LSuveVkA6LV+efFr7oimU5o2vNdC2AMCLm/505Mg==",
"integrity": "sha512-RJJbqZKd/lSDour+y1c7J6l4BWIcJ1uDBQIXqNzkST2OAoOvJngY134a55PlIzFiQtZ4+lkDi20Qcmq1a/CctA==",
"license": "MIT",
"dependencies": {
"@types/xml2js": "^0.4.8",

2
tools/pipelines-tasks/MsixSigning/package-lock.json сгенерированный
Просмотреть файл

@ -150,7 +150,7 @@
"name": "msix-tasks-helpers",
"version": "1.0.0",
"resolved": "file:../common/msix-tasks-helpers-1.0.0.tgz",
"integrity": "sha512-PzAGAz2kcID8hnqTR9qHGR52sul+aqpZl8DXzNmO5mu5J+LSuveVkA6LV+efFr7oimU5o2vNdC2AMCLm/505Mg==",
"integrity": "sha512-RJJbqZKd/lSDour+y1c7J6l4BWIcJ1uDBQIXqNzkST2OAoOvJngY134a55PlIzFiQtZ4+lkDi20Qcmq1a/CctA==",
"license": "MIT",
"dependencies": {
"@types/xml2js": "^0.4.8",

Просмотреть файл

@ -5,6 +5,7 @@ This includes the source for Azure Pipelines tasks for:
* Signing MSIX packages.
* Creating VHDX disks for use with MSIX app attach.
* Creating App Installer files.
* App Attach VHDX artifact to AVD.
## Building
@ -45,4 +46,5 @@ mocha
* MakeAppx.exe (under `.\common\lib`), SignTool.exe (under `.\MsixSigning\lib`) and related files were taken from the [MSIX Toolkit](https://github.com/microsoft/MSIX-Toolkit/tree/master/Redist.x86)
* msixmgr.exe (under `.\MsixAppAttach\lib`) was taken from the [MSIX Core 1.1. release](https://github.com/microsoft/msix-packaging/releases)
* The MSBuildHelpers at `.\MsixPackaging\MSBuildHelpers` were taken from [azure-pipelines-tasks](https://github.com/microsoft/azure-pipelines-tasks/tree/master/Tasks/Common/MSBuildHelpers). There were only a minor changes to type annotations to make it compile with our configuration.
* vswhere.exe under `.\MsixPackaging\MSBuildHelpers` was taken from [vswhere release 1.0.62](https://github.com/Microsoft/vswhere/releases/download/1.0.62/vswhere.exe)
* vswhere.exe under `.\MsixPackaging\MSBuildHelpers` was taken from [vswhere release 1.0.62](https://github.com/Microsoft/vswhere/releases/download/1.0.62/vswhere.exe)
* AppAttachFramework(AppAttachKernel.nupkg) (under `.\common\lib`) was taken from internal project that helps to build, package and app attach MSIX packages.

Просмотреть файл

@ -18,7 +18,7 @@ variables:
buildOutRoot: '$(Build.ArtifactStagingDirectory)/buildOutput'
# Version number
major: '1'
minor: '1'
minor: '2'
patch: $[counter(variables['minor'], 0)]
# Upload results of Semmle analysis
LGTM.UploadSnapshot: true

Просмотреть файл

@ -13,6 +13,7 @@ param (
$taskNames = (
"AppInstallerFile",
"AVDAppAttachPublish",
"MsixAppAttach",
"MsixPackaging",
"MsixSigning"
@ -71,6 +72,7 @@ function BuildCommonHelpers([switch]$installDependencies)
# Build the directory
Write-Host "Compiling common helpers"
npx tsc
node installAppAttachFramework.js
if (-not $?)
{
throw "Failed to build 'common'"
@ -127,6 +129,24 @@ function InstallDevelopmentTools()
# Test platform
npm install -g mocha
#Install Nuget
winget install Microsoft.Nuget
}
# Installs the development tools required to work on the repo.
# This needs Node.js v10 to already be installed, and will only install the
# required global Node modules
function InstallDevelopmentToolsForProduction()
{
# Typescript compiler
npm install -g typescript
# CLI tools to interact with Azure DevOps (e.g. build and publish the extension)
npm install -g tfx-cli
# Test platform
npm install -g mocha
}
# Installs the dependencies for every project (the common helpers and all tasks)
@ -194,7 +214,7 @@ function Build() {
# This doesn't create the extension .vsix package.
function BuildForProduction()
{
InstallDevelopmentTools
InstallDevelopmentToolsForProduction
InstallAllDepenencies
Build

Просмотреть файл

@ -0,0 +1,2 @@
installAppAttachFramework.*
NugetInstall.ps1

Просмотреть файл

@ -0,0 +1,35 @@
<#
.SYNOPSIS
This script installs the provided nuget package using nuget CLI command
.DESCRIPTION
This script installs provided nuget package with given packageId and version
.PARAMETER packageId
Nuget package Id that needs to be installed
.PARAMETER version
Nuget Package Id version that needs to be installed
.PARAMETER outputDirectory
Output path where provide nuget package has to be installed
#>
param([string]$packageId=$null, [string]$version=$null, [string]$outputDirectory)
try
{
if ($packageId -and $version)
{
& nuget install $packageId -Version $version -NonInteractive -OutputDirectory $outputDirectory
}
else
{
& nuget install 'packages.config' -NonInteractive -OutputDirectory $outputDirectory
}
}
catch
{
Write-Error "Error Occurred:"
Write-Error $_
}

Просмотреть файл

@ -8,6 +8,9 @@ import { ToolRunner } from 'azure-pipelines-task-lib/toolrunner';
export const MAKEAPPX_PATH = path.join(__dirname, 'lib', 'makeappx');
export const CLIENT_TYPE = 'AzureDevOps';
export const CLIENT_VERSION = '2.0.0';
/**
* When running on an agent, returns the value of Agent.TempDirectory which is cleaned after
* each pipeline job. When running locally, returns the local temp directory.
@ -116,4 +119,4 @@ export const writeXml = (xmlObject: any, filePath: string) =>
{
const xmlBuilder = new xml.Builder();
fs.writeFileSync(filePath, xmlBuilder.buildObject(xmlObject));
}
}

Просмотреть файл

@ -0,0 +1,105 @@
import * as path from "path";
import * as tl from "azure-pipelines-task-lib/task";
import * as fs from 'fs';
import { ToolRunner } from 'azure-pipelines-task-lib/toolrunner';
import helpers = require('./helpers');
const NUGET_INSTALL_SCRIPT = path.join(__dirname, 'NugetInstall.ps1');
const APPATTACH_FRAMEWORK_NUPKG_DIR = path.join(__dirname, 'lib');
/**
* picks installed nuget and copies target DLLs into output folder
* @param folderPath the path to the package(.nupkg or packages.config) file to be installed
* @param targetNetFramework target .Net framework to pick target and dependency DLLs complying with the package to be installed
*/
async function copyTargetDlls(folderPath: string, targetNetFramework: string): Promise<void> {
try {
// Read the contents of the folder
const folderContents = fs.readdirSync(folderPath);
for (const nugetPackFolder of folderContents) {
const libFolderContents = fs.readdirSync(path.join(folderPath, nugetPackFolder, 'lib'));
const dotNetFrameworks: string[] = ['net11', 'net20', 'net35', 'net40', 'net403', 'net45', 'net451', 'net452', 'net46', 'net461', 'net462', 'net47', 'net471', 'net472',
'net48', 'net5.0', 'net6.0', 'net7.0'];
// Find the highest version less than equal to 4.7.2 (to be compatible with AppAttachFramework)
let targetNetFolder: string | undefined;
const targetFrameworkIndx = dotNetFrameworks.indexOf(targetNetFramework);
for (let i = targetFrameworkIndx; i >= 0; i--) {
if (libFolderContents.includes(dotNetFrameworks[i])) {
targetNetFolder = dotNetFrameworks[i];
break;
}
}
if (targetNetFolder) {
// Read the contents of the target lib folder
const targetNetFolderPath = path.join(folderPath, nugetPackFolder, 'lib', targetNetFolder);
const targetNetFolderContents = fs.readdirSync(targetNetFolderPath);
// Filter DLL files
const dllFiles = targetNetFolderContents.filter(item => /\.dll$/.test(item));
// Add the full path to the DLLs
dllFiles.forEach(dll => {
const dllPath = path.join(targetNetFolderPath, dll);
tl.cp(dllPath, folderPath, undefined, true);
});
tl.rmRF(path.join(folderPath, nugetPackFolder));
}
else {
console.log("target .net dependency libraries for " + nugetPackFolder + " not found");
}
}
} catch (error) {
console.error("Error finding target framework Dlls: " + error);
}
}
/**
* installs input Nuget package into given output Location.
* @param packagePath the path to the package(.nupkg or packages.config) file to be installed
* @param outputPath output location to install the nuget package into
* @param targetNetFramework target .Net framework to pick target and dependency DLLs complying with the package to be installed
* @param packageId optional parameter, package id to be installed. Required if package install mechanism is through providing package id(instead of packages.config)
* @param version optional parameter, package version to be installed. Required if package install mechanism is through providing package id(instead of packages.config)
*/
async function installNuget(packagePath: string, outputPath: string, targetNetFramework: string, packageId?: string, version?: string) {
try {
tl.pushd(packagePath);
let powershellRunner: ToolRunner = helpers.getPowershellRunner(NUGET_INSTALL_SCRIPT);
if (packageId && version) {
powershellRunner.arg(['-packageId', packageId]);
powershellRunner.arg(['-version', version]);
}
powershellRunner.arg(['-outputDirectory', outputPath]);
let execResult = powershellRunner.execSync();
if (execResult.code) {
throw execResult.stderr;
}
await copyTargetDlls(outputPath, targetNetFramework);
} catch (error) {
console.error("Error finding target framework Dlls: " + error);
}
finally {
tl.popd();
}
}
function run(): void {
try {
const AppAttachFrameworkDll = path.join(__dirname, 'lib/AppAttachFrameworkDLL');
if (tl.exist(AppAttachFrameworkDll)) {
tl.rmRF(AppAttachFrameworkDll);
}
installNuget(APPATTACH_FRAMEWORK_NUPKG_DIR, AppAttachFrameworkDll, 'net472');
} catch (error) {
console.error(tl.loc("AppAttachFramework Install Error", error));
}
}
run();

Двоичные данные
tools/pipelines-tasks/common/lib/AppAttachKernel.1.2.1.nupkg Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,73 @@
# App Attach Framework
The App Attach Framework artifact (AppAttachKernel.nupkg) helps to Build, Package and App Attach your application. Following functionality are supported through App Attach Framework:
1. Build and MSIX Package: Build your source code, package, sign and create an App Attach ready artifact(VHDX)
2. Azure Virtual Desktop(AVD) App Attach: Publish App Attach artifact to AVD.
3. Local App Attach: This functionality is supported for your local systems where it allows to local app attach your App Attach artifact.
It supports either entire or subset of these functionalities.
# How to Use
To install App Attach Framework one needs to install `AppAttachKernel.nupkg` in their developer tool and use following configuration and APIs as mentioned below. Please note that each task takes in a `taskType` parameter whose value corresponds to the task it needs to perform.
## Build and MSIX Package
``` json5
string inputJson_BuildAndPackage =
{
"taskType": "7",
"packageLocation": "<Output Package location. This value is mentioned as absolute output folder path>",
"projectFilePath": "<Path to project file path>",
"configuration": "<Build Configuration(x64 or x86)>",
"platform": "<Build Platform (Debug or Release)>",
"certificatePath": "<Path to Certificate file for MSIX signing>",
"certificatePassword": "<Certificate Password>",
"msbuildPath": "<Path to MS Build>",
"msixManagerPath": "<Path to msixmgr.exe>",
"packageVersion": "<Package Version>",
"clientType": "<Name of client from where App Attach is being done>",
"clientVersion": "<Client Version>"
};
AppAttachKernelController appAttachKernel = new AppAttachKernelController(inputJson_BuildAndPackage);
var cancellationTokenSource = new CancellationTokenSource();
appAttachKernel.execute(cancellationTokenSource.Token);
```
## AVD App Attach
``` json5
string inputJson_AVDAppAttach =
{
"taskType": "8",
"appAttachImagePath": "<Path to VHDX app attach image>",
"emailId": "<Email of target azure user to authenticate and app attach>",
"accessToken": "<Access token to authenticate and app attach>",
"azureSubscriptionId": "<Azure subscription Id>",
"azureResourceGroup": "<Azure Resource group>",
"azureStorageAccount": "<Azure storage account for App Attach>",
"azureStorageKey": "<Storage key of target storage account to gain access to target file share",
"azureStorageFileShare": "<Azure File share to be used for app attach>",
"azureHostPoolName": "<target AVD Host pool for app attach>",
"azureWorkspace": "<target AVD work space for app attach>",
"azureApplicationGroupName": "<target AVD Application Group for app attach>",
"clientType": "<Name of client from where App Attach is being done>",
"clientVersion": "<Client Version>"
}
AppAttachKernelController appAttachKernel = new AppAttachKernelController(inputJson_AVDAppAttach);
var cancellationTokenSource = new CancellationTokenSource();
appAttachKernel.execute(cancellationTokenSource.Token);
```
## Local App Attach
``` json5
string inputJson_LocalAppAttach =
{
"taskType": "16",
"appAttachImagePath": "<Path to VHDX app attach image>",
"clientType": "<Name of client from where App Attach is being done>",
"clientVersion": "<Client Version>"
}
AppAttachKernelController appAttachKernel = new AppAttachKernelController(inputJson_LocalAppAttach);
var cancellationTokenSource = new CancellationTokenSource();
appAttachKernel.execute(cancellationTokenSource.Token);
```

Просмотреть файл

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- defaultPushSource key works like the 'defaultPushSource' key of NuGet.Config files. -->
<!-- This can be used by administrators to prevent accidental publishing of packages to nuget.org. -->
<packageRestore>
<!-- Allow NuGet to download missing packages -->
<add key="enabled" value="True" />
<!-- Automatically check for missing packages during build in Visual Studio -->
<add key="automatic" value="True" />
</packageRestore>
<!-- Default Package Sources; works like the 'packageSources' section of NuGet.Config files. -->
<!-- This collection cannot be deleted or modified but can be disabled/enabled by users. -->
<packageSources>
<clear />
<add key="AppAttachFramework.Artifacts" value="./" />
<add key="nuget.org" value="https://www.nuget.org/api/v2" />
</packageSources>
</configuration>

Просмотреть файл

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AppAttachKernel" version="1.2.1"/>
<package id="Microsoft.Extensions.Configuration" version="7.0.0"/>
<package id="Microsoft.Extensions.Configuration.Binder" version="7.0.0"/>
<package id="Microsoft.Extensions.Configuration.CommandLine" version="7.0.0"/>
<package id="Microsoft.Extensions.Configuration.EnvironmentVariables" version="7.0.0"/>
<package id="Microsoft.Extensions.Configuration.FileExtensions" version="7.0.0"/>
<package id="Microsoft.Extensions.Configuration.Json" version="7.0.0"/>
<package id="Microsoft.Extensions.Configuration.UserSecrets" version="7.0.0"/>
<package id="Microsoft.Extensions.Hosting" version="7.0.0"/>
<package id="Microsoft.Extensions.Hosting.Abstractions" version="7.0.0"/>
<package id="Microsoft.Extensions.Logging" version="7.0.0"/>
<package id="Microsoft.Extensions.Logging.Configuration" version="7.0.0"/>
<package id="Microsoft.Extensions.Logging.Console" version="7.0.0"/>
<package id="Microsoft.Extensions.Logging.Debug" version="7.0.0"/>
<package id="Microsoft.Extensions.Logging.EventLog" version="7.0.0"/>
<package id="Microsoft.Extensions.Logging.EventSource" version="7.0.0"/>
<package id="Microsoft.Extensions.Options.ConfigurationExtensions" version="7.0.0"/>
<package id="System.Text.Json" version="7.0.0"/>
<package id="WindowsAzure.Storage" version="9.3.3"/>
<package id="Microsoft.Bcl.AsyncInterfaces" version="7.0.0"/>
<package id="Microsoft.Extensions.Configuration.Abstractions" version="7.0.0"/>
<package id="Microsoft.Extensions.DependencyInjection" version="7.0.0"/>
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="7.0.0"/>
<package id="Microsoft.Extensions.FileProviders.Abstractions" version="7.0.0"/>
<package id="Microsoft.Extensions.FileProviders.Physical" version="7.0.0"/>
<package id="Microsoft.Extensions.Logging.Abstractions" version="7.0.0"/>
<package id="Microsoft.Extensions.Options" version="7.0.0"/>
<package id="Microsoft.Extensions.Primitives" version="7.0.0"/>
<package id="System.Diagnostics.DiagnosticSource" version="7.0.0"/>
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0"/>
<package id="System.Security.Cryptography.Algorithms" version="4.3.1"/>
<package id="System.Security.Cryptography.Encoding" version="4.3.0"/>
<package id="System.Security.Cryptography.Primitives" version="4.3.0"/>
<package id="System.Text.Encodings.Web" version="7.0.0"/>
<package id="System.Threading.Tasks.Extensions" version="4.5.4"/>
<package id="System.ValueTuple" version="4.5.0"/>
<package id="Newtonsoft.Json" version="13.0.3"/>
<package id="System.Buffers" version="4.5.1"/>
<package id="System.IO" version="4.3.0"/>
<package id="System.Memory" version="4.5.5"/>
<package id="System.Numerics.Vectors" version="4.5.0"/>
<package id="System.Runtime" version="4.3.0"/>
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.3"/>
<package id="Microsoft.Azure.KeyVault.Core" version="1.0.0"/>
<package id="Microsoft.Extensions.FileSystemGlobbing" version="7.0.0"/>
</packages>

2
tools/pipelines-tasks/common/package-lock.json сгенерированный
Просмотреть файл

@ -688,4 +688,4 @@
}
}
}
}
}

Просмотреть файл

@ -22,4 +22,4 @@
"@types/q": "^1.5.4",
"sync-request": "^6.1.0"
}
}
}

Просмотреть файл

@ -7,6 +7,7 @@ import shell = require('shelljs');
const taskNames: string[] =
[
'AppInstallerFile',
'AVDAppAttachPublish',
'MsixAppAttach',
'MsixPackaging',
'MsixSigning',

Просмотреть файл

@ -2,7 +2,7 @@
"manifestVersion": 1,
"id": "msix-ci-automation-task",
"name": "MSIX Packaging",
"version": "1.0.0",
"version": "1.2.0",
"publisher": "MSIX",
"targets": [
{
@ -25,25 +25,38 @@
"type": "git",
"uri": "https://github.com/microsoft/msix-packaging"
},
"files": [
"files": [
{
"path": "AppInstallerFile"
"path": "AppInstallerFile"
},
{
"path": "MsixAppAttach"
"path": "AVDAppAttachPublish"
},
{
"path": "MsixPackaging"
"path": "MsixAppAttach"
},
{
"path": "MsixSigning"
"path": "MsixPackaging"
},
{
"path": "images/msix-packaging-ext",
"addressable": true
"path": "MsixSigning"
},
{
"path": "images/msix-packaging-ext",
"addressable": true
}
],
"contributions": [
{
"id": "avd-app-attach-publish",
"type": "ms.vss-distributed-task.task",
"targets": [
"ms.vss-distributed-task.tasks"
],
"properties": {
"name": "AVDAppAttachPublish"
}
},
{
"id": "app-installer-file",
"type": "ms.vss-distributed-task.task",