-
Notifications
You must be signed in to change notification settings - Fork 37
feat: Introduce deploy command
#180
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
Merged
aviatco
merged 54 commits into
microsoft:main
from
aviatco:dev/aviatcohen/cli-cicd-intergation-main
Mar 10, 2026
Merged
Changes from all commits
Commits
Show all changes
54 commits
Select commit
Hold shift + click to select a range
b32c344
Introduce deploy command
a5f3d49
add changie
c319fb6
Resolve PR commants
a727e79
Create MSAL bridge
3c3bf67
remove metavar
c472fac
align context with other commands - will handle it in another PR
176c090
Merge pull request #2 from aviatco/deploy-command-interface
aviatco f9cb1f7
Merge branch 'dev/aviatcohen/cli-cicd-intergation-main' into dv/aviatβ¦
1a9bd67
call cicd deploy_with_config
4c53e4e
resolve PR cooments
508b7ca
Update src/fabric_cli/core/fab_auth.py
aviatco 06c2dd8
resolve commants
d844bba
remove handling of expires_on when converting msal token to azure ideβ¦
170b45a
make sure expires_on is int
a02687e
Merge pull request #3 from aviatco/dev/aviatcohen/msal-bridge
aviatco 5972eb1
support deploy command
73bbdbd
Merge branch 'dev/aviatcohen/msal-bridge' into dev/aviatcohen/deploy-β¦
e9c0fc0
using expires_in if expires_on is not avilable
d1de4ec
Merge branch 'dev/aviatcohen/cli-cicd-intergation-main' into dev/aviaβ¦
870e923
comment to disable live recording mode
4fb7b7b
change --deploy_with_config flag to config; fix PR comments
3d733cd
create deploy_setup fixture and update tests
08d7d8d
add tests
9991a00
Update src/fabric_cli/commands/fs/deploy/fab_fs_deploy_config_file.py
aviatco d3e8dba
update cicd feature flags and debug_enabled
69ef52c
Merge branch 'dev/aviatcohen/deploy-with-config' of https://github.coβ¦
d21ff0d
update cicd feature flags and debug_enabled
ca31a7b
Merge pull request #4 from aviatco/dev/aviatcohen/deploy-with-config
aviatco a2a3243
add changie
785ee13
align seccess msg with cicd updates
58acead
define VALID_DEFATULT_SCOPES at module level
a4c8c96
Merge branch 'main' into dev/aviatcohen/cli-cicd-intergation-main
aviatco c13be3d
Fix type check tests
1511aba
fix type check error for expires_on
de8660f
remove python 3.13 from test build just for testing - will be revertgβ¦
8d55167
resolve backslash issue in prompt message
a764267
fix test_get_access_token_token_error
128702c
Apply suggestion from @aviatco
aviatco 75c35f1
trying to fix interactive_cli tests
264bdbc
Merge branch 'dev/aviatcohen/cli-cicd-intergation-main' of https://giβ¦
39aa30e
deploy documentation
e6cf9d5
Merge branch 'main' into dev/aviatcohen/cli-cicd-intergation-main
aviatco 9467b6b
bump cicd version
d4f5f52
remove deploy docs
c1082b3
Merge branch 'dev/aviatcohen/cli-cicd-intergation-main' of https://giβ¦
1b8b174
revert - remove python 3.13 support
64838f0
fix deploy tests
b7bdfde
mock acquire_token method
0a8def4
mock acquire_token
7fb1e55
new decording for test_deploy_single_item_success
dc7fd75
revert scope class fixture
23f06f1
revert setup_default_format scopr class in mock_fab_set_state_config
e4cf5b2
revert setup_default_format scope class in setup_default_format
17e4a67
recorde fail tests
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| kind: added | ||
| body: Introduces the new deploy command that integrates with the fabric-cicd library, enabling users to deploy multiple Fabric items in a single | ||
| time: 2026-03-05T09:17:28.270036405Z | ||
| custom: | ||
| Author: aviatco | ||
| AuthorLink: https://github.com/aviatco |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
src/fabric_cli/commands/fs/deploy/fab_fs_deploy_config_file.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # Licensed under the MIT License. | ||
|
|
||
| import json | ||
| from argparse import Namespace | ||
|
|
||
| from fabric_cicd import append_feature_flag, configure_external_file_logging, deploy_with_config, disable_file_logging # type: ignore | ||
|
|
||
| from fabric_cli.core import fab_constant, fab_state_config | ||
| from fabric_cli.core import fab_logger | ||
| from fabric_cli.core.fab_exceptions import FabricCLIError | ||
| from fabric_cli.core.fab_msal_bridge import create_fabric_token_credential | ||
| from fabric_cli.utils import fab_ui | ||
| from fabric_cli.utils.fab_util import get_dict_from_params | ||
|
|
||
|
|
||
| def deploy_with_config_file(args: Namespace) -> None: | ||
| """deploy fabric items to a workspace using a configuration file and target environment - delegates to CICD library.""" | ||
|
|
||
| try: | ||
| if fab_state_config.get_config(fab_constant.FAB_DEBUG_ENABLED) == "true": | ||
| cli_logger = fab_logger.get_logger() | ||
| # configure file logging for CICD library to use the same file handler as the CLI | ||
| configure_external_file_logging(cli_logger) | ||
| else: | ||
| # prevent creation of a log file for fabric-cicd logs when debug mode is disabled | ||
| disable_file_logging() | ||
aviatco marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| # feature flags to avoid printing identity info in logs | ||
| append_feature_flag("disable_print_identity") | ||
|
|
||
| deploy_config_file = args.config | ||
| deploy_parameters = get_dict_from_params(args.params, max_depth=1) | ||
| for param in deploy_parameters: | ||
| if isinstance(deploy_parameters[param], str): | ||
| try: | ||
| deploy_parameters[param] = json.loads( | ||
| deploy_parameters[param]) | ||
| except json.JSONDecodeError: | ||
| # If it's not a valid JSON string, keep it as is | ||
| pass | ||
| result = deploy_with_config( | ||
| config_file_path=deploy_config_file, | ||
| environment=args.target_env, | ||
| token_credential=create_fabric_token_credential(), # MSAL bridge TokenCredential | ||
| **deploy_parameters | ||
| ) | ||
|
|
||
| if result: | ||
| fab_ui.print_output_format( | ||
| args, message=result.message) | ||
|
|
||
| except Exception as e: | ||
| raise FabricCLIError( | ||
| f"Deployment failed: {str(e)}", | ||
| fab_constant.ERROR_IN_DEPLOYMENT) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # Licensed under the MIT License. | ||
|
|
||
| from argparse import Namespace | ||
|
|
||
| from fabric_cli.commands.fs.deploy.fab_fs_deploy_config_file import deploy_with_config_file | ||
| from fabric_cli.utils import fab_ui | ||
|
|
||
|
|
||
| def exec_command(args: Namespace) -> None: | ||
| """deploy fabric items to a workspace using a configuration file and target environment - CICD flow.""" | ||
| target_env_msg = 'without a target environment' if args.target_env == 'N/A' else f"to target environment '{args.target_env}'" | ||
| if args.force or fab_ui.prompt_confirm(f"Are you sure you want to deploy {target_env_msg} using the specified configuration file?"): | ||
| deploy_with_config_file(args) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # Licensed under the MIT License. | ||
|
|
||
| import os | ||
| from typing import Optional | ||
|
|
||
| from azure.core.credentials import AccessToken, TokenCredential | ||
| from azure.core.exceptions import ClientAuthenticationError | ||
|
|
||
| from fabric_cli.core import fab_constant as con | ||
| from fabric_cli.core import fab_logger | ||
| from fabric_cli.core.fab_auth import FabAuth | ||
|
|
||
| # Bridge-specific: strict .default scope validation | ||
| VALID_DEFATULT_SCOPES = { | ||
| con.SCOPE_FABRIC_DEFAULT[0], | ||
| } | ||
|
|
||
| class MsalTokenCredential(TokenCredential): | ||
| """ | ||
| A TokenCredential implementation that wraps the existing Fabric CLI MSAL authentication. | ||
|
|
||
| This bridge uses the CLI user's existing authentication and provides it through | ||
| the Azure Identity TokenCredential interface. It handles refresh token management | ||
| automatically via MSAL's silent acquisition flow. | ||
|
|
||
| The credential will use whatever authentication the CLI user has already configured: | ||
| - User authentication (from fab auth login) | ||
| - Service principal (from environment variables) | ||
| - Managed identity (when running in Azure) | ||
| - Environment tokens (pre-acquired tokens) | ||
|
|
||
| Args: | ||
| fab_auth: FabAuth instance containing the authentication configuration. | ||
| """ | ||
|
|
||
| def __init__(self, fab_auth: FabAuth): | ||
| self._fab_auth = fab_auth | ||
|
|
||
| def get_token( | ||
| self, | ||
| *scopes: str, | ||
| claims: Optional[str] = None, | ||
| tenant_id: Optional[str] = None, | ||
| enable_cae: bool = False, | ||
| **kwargs | ||
| ) -> AccessToken: | ||
| """ | ||
| Get an access token for the specified scopes. | ||
|
|
||
| Args: | ||
| scopes: The scopes for which to request the token | ||
| claims: Optional claims challenge | ||
| tenant_id: Optional tenant ID (not used in this implementation) | ||
| enable_cae: Whether to enable Continuous Access Evaluation (not used) | ||
| **kwargs: Additional keyword arguments | ||
|
|
||
| Returns: | ||
| AccessToken object containing the token and expiration time | ||
|
|
||
| Raises: | ||
| ClientAuthenticationError: When authentication is not available | ||
| """ | ||
| for scope in scopes: | ||
| if scope not in VALID_DEFATULT_SCOPES: | ||
| fab_logger.log_debug(f"Invalid scope rejected: {scope}") | ||
| raise ClientAuthenticationError( | ||
| f"Security validation failed: requested scope is not supported." | ||
| f"Invalid scope: {scope}. " | ||
| f"Allowed scopes: {', '.join(VALID_DEFATULT_SCOPES)}" | ||
| ) | ||
| try: | ||
| msal_result = self._fab_auth.acquire_token( | ||
| list(scopes), | ||
| interactive_renew=False # Bridge is always headless | ||
| ) | ||
|
|
||
| return self._to_azure_access_token(msal_result) | ||
|
|
||
| except Exception as e: | ||
| fab_logger.log_debug(f"Token acquisition failed: {e}") | ||
| raise ClientAuthenticationError( | ||
| f"\n{str(e)}" | ||
| ) from e | ||
|
|
||
| def _to_azure_access_token(self, msal_result: dict) -> AccessToken: | ||
| """Convert MSAL result to AccessToken object.""" | ||
| access_token = msal_result["access_token"] | ||
|
|
||
| # Handle expires_on - MSAL returns Unix timestamp as string or int | ||
| expires_on = msal_result.get("expires_on") | ||
| if expires_on: | ||
| if isinstance(expires_on, str): | ||
| expires_on = int(expires_on) | ||
| else: | ||
| # Fallback: calculate from expires_in if available | ||
| expires_in = msal_result.get("expires_in") | ||
| if expires_in: | ||
| import time | ||
| expires_on = int(time.time() + expires_in) | ||
| else: | ||
| raise ClientAuthenticationError( | ||
| "Token expiration time is required but not available") | ||
| return AccessToken(access_token, int(expires_on)) | ||
|
|
||
| def close(self) -> None: | ||
| """Close the credential (no-op for this implementation).""" | ||
| pass | ||
|
|
||
|
|
||
| def create_fabric_token_credential() -> TokenCredential: | ||
| """ | ||
| Create a TokenCredential that uses the current Fabric CLI authentication. | ||
|
|
||
| This function creates a TokenCredential that wraps the existing MSAL authentication | ||
| from the Fabric CLI. It will use whatever authentication the user has already | ||
| configured (user login, service principal, managed identity, or environment tokens). | ||
| Returns: | ||
| TokenCredential that can be used with Azure SDKs | ||
|
|
||
| Raises: | ||
| ClientAuthenticationError: When no authentication is configured | ||
| """ | ||
| fab_auth = FabAuth() | ||
|
|
||
| identity_type = fab_auth.get_identity_type() | ||
|
|
||
| fab_logger.log_debug(f"Creating TokenCredential for identity type: {identity_type}") | ||
| return MsalTokenCredential(fab_auth) | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.