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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "Add custom endpoint support via a `storageEndpoint` configuration option for the Azure Storage build cache plugin.",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ export interface IAzureStorageAuthenticationOptions extends IAzureAuthentication
storageAccountName: string;
// (undocumented)
storageContainerName: string;
// (undocumented)
storageEndpoint?: string;
}

// @public (undocumented)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
export interface IAzureStorageAuthenticationOptions extends IAzureAuthenticationBaseOptions {
storageContainerName: string;
storageAccountName: string;
storageEndpoint?: string;
isCacheWriteAllowed: boolean;
}

Expand All @@ -46,7 +47,11 @@ export class AzureStorageAuthentication extends AzureAuthenticationBase {
this._storageAccountName = options.storageAccountName;
this._storageContainerName = options.storageContainerName;
this._isCacheWriteAllowedByConfiguration = options.isCacheWriteAllowed;
this._storageAccountUrl = `https://${this._storageAccountName}.blob.core.windows.net/`;
this._storageAccountUrl = options.storageEndpoint
? options.storageEndpoint.endsWith('/')
? options.storageEndpoint
: options.storageEndpoint + '/'
: `https://${this._storageAccountName}.blob.core.windows.net/`;
}

protected _getCacheIdParts(): string[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ interface IAzureBlobStorageConfigurationJson {
*/
readonly loginFlowFailover?: LoginFlowFailoverMap;

/**
* An optional custom endpoint URL for the Azure Blob Storage account.
* Use this to connect to Azurite, private endpoints, or storage emulators.
* Overrides the default endpoint derived from storageAccountName.
*/
readonly storageEndpoint?: string;

/**
* An optional prefix for cache item blob names.
*/
Expand Down Expand Up @@ -70,6 +77,7 @@ export class RushAzureStorageBuildCachePlugin implements IRushPlugin {
return new AzureStorageBuildCacheProvider({
storageAccountName: azureBlobStorageConfiguration.storageAccountName,
storageContainerName: azureBlobStorageConfiguration.storageContainerName,
storageEndpoint: azureBlobStorageConfiguration.storageEndpoint,
azureEnvironment: azureBlobStorageConfiguration.azureEnvironment,
blobPrefix: azureBlobStorageConfiguration.blobPrefix,
loginFlow: azureBlobStorageConfiguration.loginFlow,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@
"description": "An optional prefix for cache item blob names."
},

"storageEndpoint": {
"type": "string",
"description": "An optional custom endpoint URL for the Azure Blob Storage account. Use this to connect to Azurite, private endpoints, or storage emulators. When specified, this overrides the default endpoint derived from storageAccountName. Example: \"http://127.0.0.1:10000/devstoreaccount1\" or \"https://my-proxy.example.com/devstoreaccount1\"",
"format": "uri"
},

"isCacheWriteAllowed": {
"type": "boolean",
"description": "If set to true, allow writing to the cache. Defaults to false."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,40 @@ describe(AzureStorageBuildCacheProvider.name, () => {
).toThrowErrorMatchingSnapshot();
});

describe('storageEndpoint', () => {
it('uses the default endpoint when storageEndpoint is not provided', () => {
const subject: AzureStorageBuildCacheProvider = new AzureStorageBuildCacheProvider({
storageAccountName: 'storage-account',
storageContainerName: 'container-name',
isCacheWriteAllowed: false
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
expect((subject as any)._storageAccountUrl).toBe('https://storage-account.blob.core.windows.net/');
});

it('uses the custom endpoint when storageEndpoint is provided', () => {
const subject: AzureStorageBuildCacheProvider = new AzureStorageBuildCacheProvider({
storageAccountName: 'devstoreaccount1',
storageContainerName: 'container-name',
storageEndpoint: 'http://127.0.0.1:10000/devstoreaccount1',
isCacheWriteAllowed: false
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
expect((subject as any)._storageAccountUrl).toBe('http://127.0.0.1:10000/devstoreaccount1/');
});

it('preserves a trailing slash on the custom endpoint', () => {
const subject: AzureStorageBuildCacheProvider = new AzureStorageBuildCacheProvider({
storageAccountName: 'devstoreaccount1',
storageContainerName: 'container-name',
storageEndpoint: 'https://my-proxy.example.com/devstoreaccount1/',
isCacheWriteAllowed: false
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
expect((subject as any)._storageAccountUrl).toBe('https://my-proxy.example.com/devstoreaccount1/');
});
});

describe('isCacheWriteAllowed', () => {
function prepareSubject(
optionValue: boolean,
Expand Down