diff --git a/common/changes/@microsoft/rush/login-flow-failover_2025-07-09-21-02.json b/common/changes/@microsoft/rush/login-flow-failover_2025-07-09-21-02.json new file mode 100644 index 00000000000..a8d9f795c5d --- /dev/null +++ b/common/changes/@microsoft/rush/login-flow-failover_2025-07-09-21-02.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "[azure-storage-build-cache] Update build-cache.json schema to allow the full range of `loginFlow` options supported by the underlying authentication provider. Add `loginFlowFailover` option to customize fallback sequencing.", + "type": "none" + } + ], + "packageName": "@microsoft/rush" +} \ No newline at end of file diff --git a/common/reviews/api/rush-azure-storage-build-cache-plugin.api.md b/common/reviews/api/rush-azure-storage-build-cache-plugin.api.md index 572b699ae06..4bd893d8be2 100644 --- a/common/reviews/api/rush-azure-storage-build-cache-plugin.api.md +++ b/common/reviews/api/rush-azure-storage-build-cache-plugin.api.md @@ -87,9 +87,7 @@ export interface IAzureAuthenticationBaseOptions { credentialUpdateCommandForLogging?: string | undefined; // (undocumented) loginFlow?: LoginFlowType; - loginFlowFailover?: { - [key in LoginFlowType]?: LoginFlowType; - }; + loginFlowFailover?: LoginFlowFailoverMap; } // @public (undocumented) @@ -136,6 +134,11 @@ export interface ITryGetCachedCredentialOptionsThrow extends ITryGetCachedCreden expiredCredentialBehavior: 'throwError'; } +// @public (undocumented) +export type LoginFlowFailoverMap = { + readonly [LoginFlow in LoginFlowType]?: Exclude; +}; + // @public (undocumented) export type LoginFlowType = 'DeviceCode' | 'InteractiveBrowser' | 'AdoCodespacesAuth' | 'VisualStudioCode' | 'AzureCli' | 'AzureDeveloperCli' | 'AzurePowerShell'; diff --git a/libraries/rush-lib/src/schemas/build-cache.schema.json b/libraries/rush-lib/src/schemas/build-cache.schema.json index 4131a90bd6e..2c7e8fd6967 100644 --- a/libraries/rush-lib/src/schemas/build-cache.schema.json +++ b/libraries/rush-lib/src/schemas/build-cache.schema.json @@ -8,6 +8,23 @@ "items": { "$ref": "#/definitions/anything" } + }, + "entraLoginFlow": { + "type": "string", + "description": "The Primary Entra ID login flow to use. Defaults to 'AdoCodespacesAuth' on GitHub Codespaces, 'VisualStudioCode' otherwise. If this flow fails it will fall back based on the configuration in `loginFlowFailover`.", + "enum": [ + "AdoCodespacesAuth", + "InteractiveBrowser", + "DeviceCode", + "VisualStudioCode", + "AzureCli", + "AzureDeveloperCli", + "AzurePowerShell" + ] + }, + "fallbackEntraLoginFlow": { + "$ref": "#/definitions/entraLoginFlow", + "description": "The Entra ID login flow to fall back to. If null, a failure in this login mode is terminal." } }, "type": "object", @@ -55,9 +72,56 @@ "enum": ["AzurePublicCloud", "AzureChina", "AzureGermany", "AzureGovernment"] }, "loginFlow": { - "type": "string", - "description": "The Entra ID login flow to use. Defaults to 'AdoCodespacesAuth' on GitHub Codespaces, 'InteractiveBrowser' otherwise.", - "enum": ["AdoCodespacesAuth", "InteractiveBrowser", "DeviceCode"] + "$ref": "#/definitions/entraLoginFlow" + }, + "loginFlowFailover": { + "type": "object", + "description": "Optional configuration for a fallback login flow if the primary login flow fails. If not defined, the default order is: AdoCodespacesAuth -> VisualStudioCode -> AzureCli -> AzureDeveloperCli -> AzurePowerShell -> InteractiveBrowser -> DeviceCode.", + "additionalProperties": false, + "properties": { + "AdoCodespacesAuth": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["AdoCodespacesAuth"] } } + ] + }, + "InteractiveBrowser": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["InteractiveBrowser"] } } + ] + }, + "DeviceCode": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["DeviceCode"] } } + ] + }, + "VisualStudioCode": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["VisualStudioCode"] } } + ] + }, + "AzureCli": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["AzureCli"] } } + ] + }, + "AzureDeveloperCli": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["AzureDeveloperCli"] } } + ] + }, + "AzurePowerShell": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["AzurePowerShell"] } } + ] + } + } }, "blobPrefix": { "type": "string", diff --git a/rush-plugins/rush-azure-storage-build-cache-plugin/src/AzureAuthenticationBase.ts b/rush-plugins/rush-azure-storage-build-cache-plugin/src/AzureAuthenticationBase.ts index 94c6fb71984..d51757ac1f9 100644 --- a/rush-plugins/rush-azure-storage-build-cache-plugin/src/AzureAuthenticationBase.ts +++ b/rush-plugins/rush-azure-storage-build-cache-plugin/src/AzureAuthenticationBase.ts @@ -95,6 +95,13 @@ export type LoginFlowType = | 'AzureDeveloperCli' | 'AzurePowerShell'; +/** + * @public + */ +export type LoginFlowFailoverMap = { + readonly [LoginFlow in LoginFlowType]?: Exclude; +}; + /** * @public */ @@ -120,9 +127,7 @@ export interface IAzureAuthenticationBaseOptions { * } * ``` */ - loginFlowFailover?: { - [key in LoginFlowType]?: LoginFlowType; - }; + loginFlowFailover?: LoginFlowFailoverMap; } /** diff --git a/rush-plugins/rush-azure-storage-build-cache-plugin/src/RushAzureStorageBuildCachePlugin.ts b/rush-plugins/rush-azure-storage-build-cache-plugin/src/RushAzureStorageBuildCachePlugin.ts index a0f4883fcad..fe13be09eb6 100644 --- a/rush-plugins/rush-azure-storage-build-cache-plugin/src/RushAzureStorageBuildCachePlugin.ts +++ b/rush-plugins/rush-azure-storage-build-cache-plugin/src/RushAzureStorageBuildCachePlugin.ts @@ -2,7 +2,7 @@ // See LICENSE in the project root for license information. import type { IRushPlugin, RushSession, RushConfiguration } from '@rushstack/rush-sdk'; -import type { AzureEnvironmentName, LoginFlowType } from './AzureAuthenticationBase'; +import type { AzureEnvironmentName, LoginFlowFailoverMap, LoginFlowType } from './AzureAuthenticationBase'; const PLUGIN_NAME: string = 'AzureStorageBuildCachePlugin'; @@ -13,17 +13,17 @@ interface IAzureBlobStorageConfigurationJson { /** * The name of the the Azure storage account to use for build cache. */ - storageAccountName: string; + readonly storageAccountName: string; /** * The name of the container in the Azure storage account to use for build cache. */ - storageContainerName: string; + readonly storageContainerName: string; /** * The Azure environment the storage account exists in. Defaults to AzureCloud. */ - azureEnvironment?: AzureEnvironmentName; + readonly azureEnvironment?: AzureEnvironmentName; /** * Login flow to use for interactive authentication. @@ -31,20 +31,25 @@ interface IAzureBlobStorageConfigurationJson { */ readonly loginFlow?: LoginFlowType; + /** + * Fallback login flows to use if the primary login flow fails. + */ + readonly loginFlowFailover?: LoginFlowFailoverMap; + /** * An optional prefix for cache item blob names. */ - blobPrefix?: string; + readonly blobPrefix?: string; /** * If set to true, allow writing to the cache. Defaults to false. */ - isCacheWriteAllowed?: boolean; + readonly isCacheWriteAllowed?: boolean; /** * If set to true, reading the cache requires authentication. Defaults to false. */ - readRequiresAuthentication?: boolean; + readonly readRequiresAuthentication?: boolean; } /** @@ -67,6 +72,7 @@ export class RushAzureStorageBuildCachePlugin implements IRushPlugin { azureEnvironment: azureBlobStorageConfiguration.azureEnvironment, blobPrefix: azureBlobStorageConfiguration.blobPrefix, loginFlow: azureBlobStorageConfiguration.loginFlow, + loginFlowFailover: azureBlobStorageConfiguration.loginFlowFailover, isCacheWriteAllowed: !!azureBlobStorageConfiguration.isCacheWriteAllowed, readRequiresAuthentication: !!azureBlobStorageConfiguration.readRequiresAuthentication }); diff --git a/rush-plugins/rush-azure-storage-build-cache-plugin/src/index.ts b/rush-plugins/rush-azure-storage-build-cache-plugin/src/index.ts index 62540078dd0..e87b34f8819 100644 --- a/rush-plugins/rush-azure-storage-build-cache-plugin/src/index.ts +++ b/rush-plugins/rush-azure-storage-build-cache-plugin/src/index.ts @@ -8,6 +8,7 @@ export { type ICredentialResult, type AzureEnvironmentName, type LoginFlowType, + type LoginFlowFailoverMap, type ITryGetCachedCredentialOptionsBase, type ITryGetCachedCredentialOptionsLogWarning, type ITryGetCachedCredentialOptionsThrow, diff --git a/rush-plugins/rush-azure-storage-build-cache-plugin/src/schemas/azure-blob-storage-config.schema.json b/rush-plugins/rush-azure-storage-build-cache-plugin/src/schemas/azure-blob-storage-config.schema.json index d471c614172..3a5173a26fd 100644 --- a/rush-plugins/rush-azure-storage-build-cache-plugin/src/schemas/azure-blob-storage-config.schema.json +++ b/rush-plugins/rush-azure-storage-build-cache-plugin/src/schemas/azure-blob-storage-config.schema.json @@ -8,6 +8,26 @@ "required": ["storageAccountName", "storageContainerName"], + "definitions": { + "loginFlow": { + "type": "string", + "description": "The Primary Entra ID login flow to use. Defaults to 'AdoCodespacesAuth' on GitHub Codespaces, 'VisualStudioCode' otherwise. If this flow fails it will fall back based on the configuration in `loginFlowFailover`.", + "enum": [ + "AdoCodespacesAuth", + "InteractiveBrowser", + "DeviceCode", + "VisualStudioCode", + "AzureCli", + "AzureDeveloperCli", + "AzurePowerShell" + ] + }, + "fallbackLoginFlow": { + "$ref": "#/definitions/loginFlow", + "description": "The Entra ID login flow to fall back to. If null, a failure in this login mode is terminal." + } + }, + "properties": { "storageAccountName": { "type": "string", @@ -26,9 +46,48 @@ }, "loginFlow": { - "type": "string", - "description": "The Entra ID login flow to use. Defaults to 'AdoCodespacesAuth' on GitHub Codespaces, 'InteractiveBrowser' otherwise.", - "enum": ["AdoCodespacesAuth", "InteractiveBrowser", "DeviceCode"] + "$ref": "#/definitions/loginFlow" + }, + + "loginFlowFailover": { + "type": "object", + "description": "Optional configuration for a fallback login flow if the primary login flow fails. If not defined, the default order is: AdoCodespacesAuth -> VisualStudioCode -> AzureCli -> AzureDeveloperCli -> AzurePowerShell -> InteractiveBrowser -> DeviceCode.", + "additionalProperties": false, + "properties": { + "AdoCodespacesAuth": { + "allOf": [ + { "$ref": "#/definitions/fallbackLoginFlow" }, + { "not": { "enum": ["AdoCodespacesAuth"] } } + ] + }, + "InteractiveBrowser": { + "allOf": [ + { "$ref": "#/definitions/fallbackLoginFlow" }, + { "not": { "enum": ["InteractiveBrowser"] } } + ] + }, + "DeviceCode": { + "allOf": [{ "$ref": "#/definitions/fallbackLoginFlow" }, { "not": { "enum": ["DeviceCode"] } }] + }, + "VisualStudioCode": { + "allOf": [ + { "$ref": "#/definitions/fallbackLoginFlow" }, + { "not": { "enum": ["VisualStudioCode"] } } + ] + }, + "AzureCli": { + "allOf": [{ "$ref": "#/definitions/fallbackLoginFlow" }, { "not": { "enum": ["AzureCli"] } }] + }, + "AzureDeveloperCli": { + "allOf": [ + { "$ref": "#/definitions/fallbackLoginFlow" }, + { "not": { "enum": ["AzureDeveloperCli"] } } + ] + }, + "AzurePowerShell": { + "allOf": [{ "$ref": "#/definitions/fallbackLoginFlow" }, { "not": { "enum": ["AzurePowerShell"] } }] + } + } }, "blobPrefix": {