refactor: replace requireVariable and requireInput with task-lib equivalents and improve error handling

This commit is contained in:
2026-02-25 21:12:40 +01:00
parent 8e9224cff9
commit 2c2981d791
10 changed files with 59 additions and 91 deletions

View File

@@ -1,18 +1,26 @@
import * as tl from 'azure-pipelines-task-lib/task';
import { import {
buildOidcUrl, buildOidcUrl,
exchangeOidcForScopedToken, exchangeOidcForScopedToken,
getServiceConnectionMetadata, getServiceConnectionMetadata,
requestOidcToken requestOidcToken
} from './oidc'; } from './oidc';
import { requireVariable } from './devops-helpers';
export const STORAGE_SCOPE = 'https://storage.azure.com/.default'; export const STORAGE_SCOPE = 'https://storage.azure.com/.default';
export async function requestStorageAccessToken( export async function requestStorageAccessToken(
endpointId: string endpointId: string
): Promise<string> { ): Promise<string> {
const oidcBaseUrl = requireVariable('System.OidcRequestUri'); const oidcBaseUrl = tl.getVariable('System.OidcRequestUri');
const systemAccessToken = requireVariable('System.AccessToken'); const systemAccessToken = tl.getVariable('System.AccessToken');
if (oidcBaseUrl === undefined) {
throw new Error('Missing required pipeline variable: System.OidcRequestUri.');
}
if (systemAccessToken === undefined) {
throw new Error('Missing required pipeline variable: System.AccessToken.');
}
const metadata = getServiceConnectionMetadata(endpointId); const metadata = getServiceConnectionMetadata(endpointId);

View File

@@ -1,28 +0,0 @@
type TaskLibBridge = {
getInput: (name: string, required?: boolean) => string | undefined;
getVariable: (name: string) => string | undefined;
};
function getTaskLibBridge(): TaskLibBridge {
return require('azure-pipelines-task-lib/task') as TaskLibBridge;
}
export function requireInput(name: string): string {
const taskLib = getTaskLibBridge();
const value = taskLib.getInput(name, true);
if (!value) {
throw new Error(`Task input ${name} is required.`);
}
return value.trim();
}
export function requireVariable(name: string): string {
const taskLib = getTaskLibBridge();
const value = taskLib.getVariable(name);
if (!value) {
throw new Error(`Missing required pipeline variable: ${name}.`);
}
return value.trim();
}

View File

@@ -1,3 +1,2 @@
export * from './devops-helpers';
export * from './oidc'; export * from './oidc';
export * from './blob'; export * from './blob';

View File

@@ -1,3 +1,5 @@
import * as tl from 'azure-pipelines-task-lib/task';
export type ServiceConnectionMetadata = { export type ServiceConnectionMetadata = {
tenantId: string; tenantId: string;
clientId: string; clientId: string;
@@ -11,40 +13,25 @@ export type TokenResponse = {
export const CLIENT_ASSERTION_TYPE = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'; export const CLIENT_ASSERTION_TYPE = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer';
type TaskLibEndpointBridge = {
getEndpointAuthorizationParameter: (
endpointId: string,
key: string,
optional: boolean
) => string | undefined;
getEndpointDataParameter: (endpointId: string, key: string, optional: boolean) => string | undefined;
};
type OidcResponse = { type OidcResponse = {
oidcToken?: string; oidcToken?: string;
}; };
function getTaskLibEndpointBridge(): TaskLibEndpointBridge {
return require('azure-pipelines-task-lib/task') as TaskLibEndpointBridge;
}
export function getServiceConnectionMetadata(endpointId: string): ServiceConnectionMetadata { export function getServiceConnectionMetadata(endpointId: string): ServiceConnectionMetadata {
const taskLib = getTaskLibEndpointBridge();
const tenantId = const tenantId =
taskLib.getEndpointAuthorizationParameter(endpointId, 'tenantid', true) || tl.getEndpointAuthorizationParameter(endpointId, 'tenantid', true) ||
taskLib.getEndpointDataParameter(endpointId, 'tenantid', true); tl.getEndpointDataParameter(endpointId, 'tenantid', true);
const clientId = const clientId =
taskLib.getEndpointAuthorizationParameter(endpointId, 'serviceprincipalid', true) || tl.getEndpointAuthorizationParameter(endpointId, 'serviceprincipalid', true) ||
taskLib.getEndpointAuthorizationParameter(endpointId, 'clientid', true) || tl.getEndpointAuthorizationParameter(endpointId, 'clientid', true) ||
taskLib.getEndpointDataParameter(endpointId, 'serviceprincipalid', true); tl.getEndpointDataParameter(endpointId, 'serviceprincipalid', true);
if (!tenantId) { if (tenantId === undefined) {
throw new Error('Could not resolve tenant ID from the selected AzureRM service connection.'); throw new Error('Could not resolve tenant ID from the selected AzureRM service connection.');
} }
if (!clientId) { if (clientId === undefined) {
throw new Error('Could not resolve client ID from the selected AzureRM service connection.'); throw new Error('Could not resolve client ID from the selected AzureRM service connection.');
} }

View File

@@ -4,21 +4,27 @@ import {
buildOidcUrl, buildOidcUrl,
exchangeOidcForScopedToken, exchangeOidcForScopedToken,
getServiceConnectionMetadata, getServiceConnectionMetadata,
requestOidcToken, requestOidcToken
requireInput,
requireVariable
} from '@skoszewski/ado-sk-toolkit-shared'; } from '@skoszewski/ado-sk-toolkit-shared';
const AZDO_APP_SCOPE = '499b84ac-1321-427f-aa17-267ca6975798/.default'; const AZDO_APP_SCOPE = '499b84ac-1321-427f-aa17-267ca6975798/.default';
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
const endpointId = requireInput('serviceConnectionARM'); const endpointId = tl.getInputRequired('serviceConnectionARM');
const setGitAccessToken = tl.getBoolInput('setGitAccessToken', false); const setGitAccessToken = tl.getBoolInput('setGitAccessToken', false);
const printTokenHashes = tl.getBoolInput('printTokenHashes', false); const printTokenHashes = tl.getBoolInput('printTokenHashes', false);
const oidcBaseUrl = requireVariable('System.OidcRequestUri'); const oidcBaseUrl = tl.getVariable('System.OidcRequestUri');
const accessToken = requireVariable('System.AccessToken'); const accessToken = tl.getVariable('System.AccessToken');
if (oidcBaseUrl === undefined) {
throw new Error('Missing required pipeline variable: System.OidcRequestUri.');
}
if (accessToken === undefined) {
throw new Error('Missing required pipeline variable: System.AccessToken.');
}
console.log('Requesting OIDC token for ARM authentication...'); console.log('Requesting OIDC token for ARM authentication...');

View File

@@ -1,8 +1,7 @@
import * as tl from 'azure-pipelines-task-lib/task'; import * as tl from 'azure-pipelines-task-lib/task';
import { import {
buildBlobUrl, buildBlobUrl,
requestStorageAccessToken, requestStorageAccessToken
requireInput
} from '@skoszewski/ado-sk-toolkit-shared'; } from '@skoszewski/ado-sk-toolkit-shared';
async function copyBlob( async function copyBlob(
@@ -38,12 +37,12 @@ async function copyBlob(
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
const endpointId = requireInput('serviceConnectionARM'); const endpointId = tl.getInputRequired('serviceConnectionARM');
const srcStorageAccountName = requireInput('srcStorageAccountName'); const srcStorageAccountName = tl.getInputRequired('srcStorageAccountName');
const dstStorageAccountName = requireInput('dstStorageAccountName'); const dstStorageAccountName = tl.getInputRequired('dstStorageAccountName');
const srcContainerName = requireInput('srcContainerName'); const srcContainerName = tl.getInputRequired('srcContainerName');
const dstContainerNameInput = tl.getInput('dstContainerName', false)?.trim() || ''; const dstContainerNameInput = tl.getInput('dstContainerName', false) || '';
const blobName = requireInput('blobName'); const blobName = tl.getInputRequired('blobName');
console.log('Requesting storage access token from Microsoft Entra ID...'); console.log('Requesting storage access token from Microsoft Entra ID...');
const accessToken = await requestStorageAccessToken(endpointId); const accessToken = await requestStorageAccessToken(endpointId);

View File

@@ -3,8 +3,7 @@ import * as path from 'node:path';
import * as tl from 'azure-pipelines-task-lib/task'; import * as tl from 'azure-pipelines-task-lib/task';
import { import {
buildBlobUrl, buildBlobUrl,
requestStorageAccessToken, requestStorageAccessToken
requireInput
} from '@skoszewski/ado-sk-toolkit-shared'; } from '@skoszewski/ado-sk-toolkit-shared';
async function downloadBlob(blobUrl: string, bearerToken: string): Promise<Buffer> { async function downloadBlob(blobUrl: string, bearerToken: string): Promise<Buffer> {
@@ -28,11 +27,11 @@ async function downloadBlob(blobUrl: string, bearerToken: string): Promise<Buffe
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
const endpointId = requireInput('serviceConnectionARM'); const endpointId = tl.getInputRequired('serviceConnectionARM');
const storageAccountName = requireInput('storageAccountName'); const storageAccountName = tl.getInputRequired('storageAccountName');
const containerName = requireInput('containerName'); const containerName = tl.getInputRequired('containerName');
const blobName = requireInput('blobName'); const blobName = tl.getInputRequired('blobName');
const destinationPath = requireInput('destinationPath'); const destinationPath = tl.getInputRequired('destinationPath');
console.log('Requesting storage access token from Microsoft Entra ID...'); console.log('Requesting storage access token from Microsoft Entra ID...');
const accessToken = await requestStorageAccessToken(endpointId); const accessToken = await requestStorageAccessToken(endpointId);

View File

@@ -1,7 +1,6 @@
import * as tl from 'azure-pipelines-task-lib/task'; import * as tl from 'azure-pipelines-task-lib/task';
import { import {
requestStorageAccessToken, requestStorageAccessToken
requireInput
} from '@skoszewski/ado-sk-toolkit-shared'; } from '@skoszewski/ado-sk-toolkit-shared';
function decodeXmlValue(value: string): string { function decodeXmlValue(value: string): string {
@@ -60,11 +59,11 @@ async function listBlobs(listUrl: string, bearerToken: string): Promise<string[]
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
const endpointId = requireInput('serviceConnectionARM'); const endpointId = tl.getInputRequired('serviceConnectionARM');
const storageAccountName = requireInput('storageAccountName'); const storageAccountName = tl.getInputRequired('storageAccountName');
const containerName = requireInput('containerName'); const containerName = tl.getInputRequired('containerName');
const prefix = tl.getInput('prefix', false)?.trim() || ''; const prefix = tl.getInput('prefix', false) || '';
const maxResultsRaw = tl.getInput('maxResults', false)?.trim() || '1000'; const maxResultsRaw = tl.getInput('maxResults', false) || '1000';
const maxResults = Number.parseInt(maxResultsRaw, 10); const maxResults = Number.parseInt(maxResultsRaw, 10);
if (!Number.isInteger(maxResults) || maxResults <= 0) { if (!Number.isInteger(maxResults) || maxResults <= 0) {

View File

@@ -2,8 +2,7 @@ import * as fs from 'node:fs/promises';
import * as tl from 'azure-pipelines-task-lib/task'; import * as tl from 'azure-pipelines-task-lib/task';
import { import {
buildBlobUrl, buildBlobUrl,
requestStorageAccessToken, requestStorageAccessToken
requireInput
} from '@skoszewski/ado-sk-toolkit-shared'; } from '@skoszewski/ado-sk-toolkit-shared';
async function uploadBlob( async function uploadBlob(
@@ -35,12 +34,12 @@ async function uploadBlob(
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
const endpointId = requireInput('serviceConnectionARM'); const endpointId = tl.getInputRequired('serviceConnectionARM');
const storageAccountName = requireInput('storageAccountName'); const storageAccountName = tl.getInputRequired('storageAccountName');
const containerName = requireInput('containerName'); const containerName = tl.getInputRequired('containerName');
const blobName = requireInput('blobName'); const blobName = tl.getInputRequired('blobName');
const sourcePath = requireInput('sourcePath'); const sourcePath = tl.getInputRequired('sourcePath');
const contentType = tl.getInput('contentType', false)?.trim() || 'application/octet-stream'; const contentType = tl.getInput('contentType', false) || 'application/octet-stream';
console.log('Requesting storage access token from Microsoft Entra ID...'); console.log('Requesting storage access token from Microsoft Entra ID...');
const accessToken = await requestStorageAccessToken(endpointId); const accessToken = await requestStorageAccessToken(endpointId);

View File

@@ -2,7 +2,7 @@
"manifestVersion": 1, "manifestVersion": 1,
"id": "sk-azure-devops-toolkit", "id": "sk-azure-devops-toolkit",
"name": "SK Azure DevOps Toolkit", "name": "SK Azure DevOps Toolkit",
"version": "1.0.5", "version": "1.1.0",
"publisher": "skoszewski-lab", "publisher": "skoszewski-lab",
"targets": [ "targets": [
{ {