Skip to content

Conversation

@DomAyre
Copy link
Contributor

@DomAyre DomAyre commented Nov 12, 2025

Why

This is a tiny step in addressing

As an extension developer, I authored a PR which broke my extension by introducing code which was missing an __init__.py file which stopped it being included in the built extension package

This wasn't caught by CI because it doesn't run any testing on the built package wheel, only that a wheel can be built without errors.

Ideally all the tests we define as extension authors should run on the thing that will ultimately be released, but the way we have to run our testing through azdev makes this non trivial as azdev doesn't support installing extensions from source wheel.

So for now, we can do better than nothing by running the extension with --help which should at least catch broken imports like my case.

How

  • Update ci/test_source.py::test_source_wheels() to optionally output the wheels to a provided dir
  • Update ci/test_source.py::test_extension() to optionally install built wheels and attempt to run that extension
  • When running the file directly, run test_source_wheels() before test_extension() with a tempdir containing the wheels

This checklist is used to make sure that common guidelines for a pull request are followed.

Related command

General Guidelines

  • Have you run azdev style <YOUR_EXT> locally? (pip install azdev required)
  • Have you run python scripts/ci/test_index.py -q locally? (pip install wheel==0.30.0 required)
  • My extension version conforms to the Extension version schema

Copilot AI review requested due to automatic review settings November 12, 2025 16:04
@azure-client-tools-bot-prd
Copy link

azure-client-tools-bot-prd bot commented Nov 12, 2025

️✔️Azure CLI Extensions Breaking Change Test
️✔️Non Breaking Changes

@azure-client-tools-bot-prd
Copy link

Hi @DomAyre,
Please write the description of changes which can be perceived by customers into HISTORY.rst.
If you want to release a new extension version, please update the version in setup.py as well.

@yonzhan
Copy link
Collaborator

yonzhan commented Nov 12, 2025

Thank you for your contribution! We will review the pull request and get back to you soon.

@github-actions
Copy link

The git hooks are available for azure-cli and azure-cli-extensions repos. They could help you run required checks before creating the PR.

Please sync the latest code with latest dev branch (for azure-cli) or main branch (for azure-cli-extensions).
After that please run the following commands to enable git hooks:

pip install azdev --upgrade
azdev setup -c <your azure-cli repo path> -r <your azure-cli-extensions repo path>

@DomAyre DomAyre changed the title Test built wheel Test built extension wheels in CI Nov 12, 2025
@github-actions
Copy link

Copilot finished reviewing on behalf of DomAyre November 12, 2025 16:06
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enhances CI testing by adding validation of built extension wheels. Instead of only verifying that wheels can be built, it now installs built wheels and tests them with --help to catch import errors and missing files (like missing __init__.py).

Key changes:

  • Modified test_source_wheels() and test_extension() to accept an optional wheel directory parameter
  • Added wheel installation and basic smoke testing after building wheels
  • Changed execution flow to build wheels first, then test both source and built wheels

check_output(['python', 'setup.py', 'bdist_wheel', '-q', '-d', built_whl_dir], cwd=s)
check_output(['azdev', 'extension', 'build', ext_name, '--dist-dir', built_whl_dir])
except CalledProcessError as err:
raise("Unable to build extension {} : {}".format(s, err))
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The raise statement should instantiate an exception, not call it as a function. This should be raise RuntimeError(...) or similar exception class.

Suggested change
raise("Unable to build extension {} : {}".format(s, err))
raise RuntimeError("Unable to build extension {} : {}".format(s, err))

Copilot uses AI. Check for mistakes.
def test_source_wheels():
if whl_dir is not None:
logger.info(f'installing extension wheel: {ext_name}')
wheel_path = next(whl_dir.glob(f"{ext_name}*.whl"))
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The next() call will raise StopIteration if no matching wheel file is found. Add a default value or handle the exception to provide a more informative error message. Consider: wheel_path = next(whl_dir.glob(f\"{ext_name}*.whl\"), None) followed by a check.

Suggested change
wheel_path = next(whl_dir.glob(f"{ext_name}*.whl"))
wheel_path = next(whl_dir.glob(f"{ext_name}*.whl"), None)
if wheel_path is None:
logger.error(f"No wheel file found for extension '{ext_name}' in directory '{whl_dir}'.")
raise FileNotFoundError(f"No wheel file found for extension '{ext_name}' in directory '{whl_dir}'.")

Copilot uses AI. Check for mistakes.
logger.info(f'installing extension wheel: {ext_name}')
wheel_path = next(whl_dir.glob(f"{ext_name}*.whl"))
subprocess.run(
f"az extension add -y -s {wheel_path}".split(" "),
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using .split(\" \") will incorrectly split paths that contain spaces. Use a list directly: [\"az\", \"extension\", \"add\", \"-y\", \"-s\", str(wheel_path)]

Suggested change
f"az extension add -y -s {wheel_path}".split(" "),
["az", "extension", "add", "-y", "-s", str(wheel_path)],

Copilot uses AI. Check for mistakes.
check=True,
)
subprocess.run(
f"az {ext_name} --help".split(" "),
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using .split(\" \") will incorrectly split extension names that contain spaces (if any exist). Use a list directly: [\"az\", ext_name, \"--help\"]

Suggested change
f"az {ext_name} --help".split(" "),
["az", ext_name, "--help"],

Copilot uses AI. Check for mistakes.
stderr=subprocess.DEVNULL,
)
subprocess.run(
f"az extension remove -n {ext_name}".split(" "),
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using .split(\" \") will incorrectly split extension names that contain spaces (if any exist). Use a list directly: [\"az\", \"extension\", \"remove\", \"-n\", ext_name]

Suggested change
f"az extension remove -n {ext_name}".split(" "),
["az", "extension", "remove", "-n", ext_name],

Copilot uses AI. Check for mistakes.
if wheels_out_dir:
wheels_out_dirs = [
os.environ.get('WHEELS_OUTPUT_DIR'),
str(whl_dir),
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When whl_dir is None, str(whl_dir) converts it to the string \"None\" instead of keeping it as None. This bypasses the null check on line 130. Use str(whl_dir) if whl_dir else None instead.

Suggested change
str(whl_dir),
str(whl_dir) if whl_dir else None,

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants