From 143b49d5f6234ef84603dcdbfe484160912b2354 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Wed, 21 May 2025 11:35:56 -0700 Subject: [PATCH 1/7] wording --- .github/copilot-instructions.md | 243 +++++++++++++++++++++++++++---- .vscode/mcp.json | 14 -- tools/mcp/typespec-story/main.py | 15 +- tools/mcp/validation/main.py | 34 +++-- 4 files changed, 244 insertions(+), 62 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 488040a26a72..796041a4b5e6 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -5,44 +5,235 @@ - DO check this [website](https://azure.github.io/azure-sdk/python_design.html), and link to pages found there, if possible, when asked about guidelines, or guidance on how to write SDKs. The general guidelines for SDK in this repo are defined there. - DO ensure folks have the appropriate setup when working with this repository. Use the verify_setup tool in the azure-sdk-validation server. -When someone asks to run validation on their library, ask them what supported environments they would like to run, such as pylint or mypy. Use the tox tool to run these. # Generating an SDK From TypeSpec +## Agent Context +- Check if there are any TypeSpec project paths in the context. If there are, use those paths to locally generate the SDK from the tsp-config.yaml file. If there +are no TypeSpec project paths in the context, ask the user for the path to the tsp-config.yaml file. If the user does not have a path, ask them to provide one. +## Prerequisites +- The user should have a GitHub account and be logged in to GitHub using the GitHub CLI `gh auth login`. +- The user should have a GitHub Personal Access Token (PAT) with the `repo` scope. -Initialize and validate a TypeSpec client library for Azure SDK for Python. Please: +## Basic Rules: +### When running tsp-client commands: +- If syncing from a local repo, do not grab a commit hash. +- Do not manually create directories. The command will create the directories for you. +- If asked to sync or generate `package-name` we need to find the path to the package's tsp-location.yaml + in the azure-sdk-for-python repo and run the command in the same directory. +- If provided a url to a tspconfig.yaml ensure it has the most recent commit hash of the tspconfig.yaml file + instead of a branch name like `main`. If the url does not have a commit hash, use the GitHub API to get the most recent commit hash of the tspconfig.yaml file. + If you are unable to do this, ask the user to provide the correct url. + `curl -s "https://api.github.com/repos/Azure/azure-rest-api-specs/commits?path=,path to tspconfig.yaml>&per_page=1"` +- Ensure that node, python, tox, and the required dependencies are installed in your environment (@azure-tools/typespec-client-generator-cli) -1. If the typespec mcp tool is available use that for generating or initializing. If asked to update changes from local spec repo, lets find the path to that local azure-rest-api-specs repo for our tools. Otherwise please follow the instructions below to generate an SDK from TypeSpec. - - Ensure that node, python, and the required dependencies are installed in your environment (@azure-tools/typespec-client-generator-cli) - - If provided a url to a tspconfig.yaml ensure it has the most recent commit hash of the tspconfig.yaml file instead of a branch name like `main`. If the url does not have a commit hash, use the GitHub API to get the most recent commit hash of the tspconfig.yaml file. If you are unable to do this, ask the user to provide the correct url. `curl -s "https://api.github.com/repos/Azure/azure-rest-api-specs/commits?path=,path to tspconfig.yaml>&per_page=1"` helpful. - - If prompted to initialize the SDK from a url or a local repo use the command `npx @azure-tools/typespec-client-generator-cli init --tsp-config [URL or local azure-rest-api-specs path to tspconfig.yaml]`. The tsp-client init command runs sync and generate under the hood, so generation is complete after the init concludes. If the tspconfig.yaml value is a path to a local file, you can mention that the user will have to populate the values in tsp-location.yaml themselves. - - If prompted to update the SDK use the command `npx @azure-tools/typespec-client-generator-cli update`. - - If prompted to generate the SDK use the command `npx @azure-tools/typespec-client-generator-cli generate`. - - If prompted to sync the SDK use the command `npx @azure-tools/typespec-client-generator-cli sync`. - - If prompted to sync with local code use the command `npx @azure-tools/typespec-client-generator-cli sync --local-spec-repo [path to local azure-rest-api-specs repo]`. - - If syncing from a local repo, do not grab a commit hash. - - Do not manually create directories. The command will create the directories for you. - - If asked to sync or generate `package-name` we need to find the path to the package's tsp-location.yaml in the azure-sdk-for-python repo and run the command in the same directory. -2. After generation is complete, validate the output by: +## Steps to Generate: + +### Step 1: Validate the correct environment is set up +- Check if the user has the correct environment set up. If not, guide them to set it up. +- Using the `verify_setup` tool in the azure-sdk-validation server is a good way to do this. + +### Step 2: Run the correct tsp-client command(s): +- The typspec-python mcp server tools should be used to run the commands. +- If any of the commands fail, check the error message and guide the user to fix the issue. + - If a command fails due to a TypeSpec error, direct the user back to the TypeSpec to fix the error. +- If the user is generating a new package, ensure that the package name is valid and follows the naming conventions for Python packages. + +### Step 3: Validate the generated SDK and Fix the issues + - Installing the newly generated package and its dev_requirements in a .venv and installing tox. + - Use the tox mcp tool from the azure-sdk-validation server to run the following validations when possible: + - Running pylint validation using tox: `tox -e pylint -c [path to tox.ini] --root .` + - Running mypy type checking using tox: `tox -e mypy -c [path to tox.ini] --root .` + - Running pyright validation using tox: `tox -e pyright -c [path to tox.ini] --root .` + - Running verifytypes validation using tox: `tox -e verifytypes -c [path to tox.ini] --root .` + - Fix issues found during validation. + - If there are any issues that cannot be fixed, please ask the user to fix them and then come back to proceed with the next step. + +### Step 4: Post-Processing of the SDK +- Create a CHANGELOG.md entry for the changes made. If there is no CHANGELOG.md file, create one in the root directory of the package. +The CHANGELOG entry should look like: + +```markdown +# Release History + +## 1.0.0 (YYYY-MM-DD) + +### Features Added + - Added a new feature to do X. + +### Breaking Changes + - Changed the way Y is done, which may break existing code that relies on the old behavior. + +### Bugs Fixed + - Fixed a bug that caused Z to not work as expected. + +### Other Changes + - Updated the documentation to reflect the new changes. + - Refactored the code to improve readability and maintainability. +``` + +### Step 5: Commit and Push the Changes +- Display the list of changed files in the repository and prompt the user to confirm the changes. Ignore uncommitted changes in .github and .vscode folders. + - If the user confirms: + - Prompt the user to commit the changes: + - Run `git add ` to stage the changes. + - Run `git commit -m ""` to commit the changes. + - Push the changes to the GitHub remote, ensuring the branch name is not "main." + - Run `git push -u origin ` to push the changes. + - If the push fails due to authentication, prompt the user to run `gh auth login` and retry the push command. + - If the user does not confirm, prompt them to fix the changes and re-run validation. + +### Step 6: Manage Pull Requests +- Check if a pull request exists for the current branch: + - If a pull request exists, inform the user and display its details. + - If no pull request exists: + - Ensure the current branch name is not "main." If it is, prompt the user to create a new branch using `git checkout -b `. + - Push the changes to the remote branch. If the branch does not exist on GitHub, create it and push the changes. + - Generate a title and description for the pull request based on the changes. Prompt the user to confirm or edit them. + - Prompt the user to select the target branch for the pull request, defaulting to "main." + - Create the pull request in DRAFT mode with the specified project, target branch, title, and description. + - Retrieve and display the pull request summary, including its status, checks, and comments. Highlight any action items. + - Return the link to the pull request for the user to review. + +### Step 7: Finalize the Process + - Prompt the user to review the pull request and make any necessary changes. + - If the user is satisfied with the pull request guide them to go back to the TypeSpec project and make any necessary changes. + + +# Pylint + +## Running Pylint +- When asked how to run pylint, or given a command to run pylint, DO check [this website](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/pylint_checking.md) and guide the user based on the information you find there. +- Do run pylint using the command `tox -e pylint --c --root .` if the user is working on a specific file. The path to the tox.ini file by default is `azure-sdk-for-python/eng/tox/tox.ini` +- For formatting the tox command DO check [this website](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/tests.md#tox) and guide the user based on the information you find there. + + + +## Fixing Pylint Warnings + +### Dos and Don'ts +- DO use the table in https://github.com/Azure/azure-sdk-tools/blob/main/tools/pylint-extensions/azure-pylint-guidelines-checker/README.md and the code examples as a guide on how to fix each rule. +- DO refer to the pylint documentation: https://pylint.readthedocs.io/en/stable/user_guide/checkers/features.html. + + +- DO NOT solve a pylint warning if you are not 100% confident about the answer. If you think your approach might not be the best, stop trying to fix the warning and leave it as is. +- DO NOT create a new file when solving a pylint error, all solutions must remain in the current file. +- DO NOT import a module or modules that do not exist to solve a pylint warning. +- DO NOT add new dependencies or imports to the project to solve a pylint warning. +- DO NOT make larger changes where a smaller change would fix the issue. +- DO NOT change the code style or formatting of the code unless it is necessary to fix a pylint warning. +- DO NOT delete code or files unless it is necessary to fix a warning. + + +# MyPy + +## Running MyPy and Fixing MyPy Warnings +- When asked how to run mypy, or given a command to run mypy, DO check [this website](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/tests.md#tox) and guide the user based on the information you find there. +- Do check this website on guidance on how to best fix MyPy issues [website link](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/static_type_checking_cheat_sheet.md) +- DO use a python 3.9 environment that is compatible with the code you are working on. If you are not sure, please ask the user for the python version they are using. # Copilot Instructions + + +# General Repository Guidelines +- DO check this [website](https://azure.github.io/azure-sdk/python_design.html), and link to pages found there, if possible, when asked about guidelines, or guidance on how to write SDKs. The general guidelines for SDK in this repo are defined there. +- DO ensure folks have the appropriate setup when working with this repository. Use the verify_setup tool in the azure-sdk-validation server. + + +# Generating an SDK From TypeSpec + +## Agent Context +- Check if there are any TypeSpec project paths in the context. If there are, use those paths to locally generate the SDK from the tsp-config.yaml file. If there +are no TypeSpec project paths in the context, ask the user for the path to the tsp-config.yaml file. If the user does not have a path, ask them to provide one. + +## Prerequisites +- The user should have a GitHub account and be logged in to GitHub using the GitHub CLI `gh auth login`. +- The user should have a GitHub Personal Access Token (PAT) with the `repo` scope. + +## Basic Rules: +### When running tsp-client commands: +- If syncing from a local repo, do not grab a commit hash. +- Do not manually create directories. The command will create the directories for you. +- If asked to sync or generate `package-name` we need to find the path to the package's tsp-location.yaml + in the azure-sdk-for-python repo and run the command in the same directory. +- If provided a url to a tspconfig.yaml ensure it has the most recent commit hash of the tspconfig.yaml file + instead of a branch name like `main`. If the url does not have a commit hash, use the GitHub API to get the most recent commit hash of the tspconfig.yaml file. + If you are unable to do this, ask the user to provide the correct url. + `curl -s "https://api.github.com/repos/Azure/azure-rest-api-specs/commits?path=,path to tspconfig.yaml>&per_page=1"` +- Ensure that node, python, tox, and the required dependencies are installed in your environment (@azure-tools/typespec-client-generator-cli) + + +## Steps to Generate: + +### Step 1: Validate the correct environment is set up +- Check if the user has the correct environment set up. If not, guide them to set it up. +- Using the `verify_setup` tool in the azure-sdk-validation server is a good way to do this. + +### Step 2: Run the correct tsp-client command(s): +- The typspec-python mcp server tools should be used to run the commands. +- If any of the commands fail, check the error message and guide the user to fix the issue. + - If a command fails due to a TypeSpec error, direct the user back to the TypeSpec to fix the error. +- If the user is generating a new package, ensure that the package name is valid and follows the naming conventions for Python packages. + +### Step 3: Validate the generated SDK and Fix the issues - Installing the newly generated package and its dev_requirements in a .venv and installing tox. - - Running pylint validation using tox: `tox -e pylint -c [path to tox.ini] --root .`. Or use the tox mcp tool from the azure-sdk-validation server. - - Running mypy type checking using tox: `tox -e mypy -c [path to tox.ini] --root .`. Or use the tox mcp tool from the azure-sdk-validation server. - - Running pyright validation using tox: `tox -e pyright -c [path to tox.ini] --root .`. Or use the tox mcp tool from the azure-sdk-validation server. - - Running verifytypes validation using tox: `tox -e verifytypes -c [path to tox.ini] --root .`. Or use the tox mcp tool from the azure-sdk-validation server. + - Use the tox mcp tool from the azure-sdk-validation server to run the following validations when possible: + - Running pylint validation using tox: `tox -e pylint -c [path to tox.ini] --root .` + - Running mypy type checking using tox: `tox -e mypy -c [path to tox.ini] --root .` + - Running pyright validation using tox: `tox -e pyright -c [path to tox.ini] --root .` + - Running verifytypes validation using tox: `tox -e verifytypes -c [path to tox.ini] --root .` + - Fix issues found during validation. + - If there are any issues that cannot be fixed, please ask the user to fix them and then come back to proceed with the next step. + +### Step 4: Post-Processing of the SDK +- Create a CHANGELOG.md entry for the changes made. If there is no CHANGELOG.md file, create one in the root directory of the package. +The CHANGELOG entry should look like: + +```markdown +# Release History + +## 1.0.0 (YYYY-MM-DD) + +### Features Added + - Added a new feature to do X. + +### Breaking Changes + - Changed the way Y is done, which may break existing code that relies on the old behavior. + +### Bugs Fixed + - Fixed a bug that caused Z to not work as expected. -3. If any errors or warnings are found, after running all validation checks provide guidance on fixing them following Azure SDK best practices. +### Other Changes + - Updated the documentation to reflect the new changes. + - Refactored the code to improve readability and maintainability. +``` -4. After the above steps, let's use the GitHub mcp tool to create a pull request with the changes. The pull request should include: - - A title that describes the changes made. - - A description that includes the following: - - A summary of the changes made. - - A list of any issues or warnings found during validation and how they were fixed. - - Any additional notes or comments about the changes made. +### Step 5: Commit and Push the Changes +- Display the list of changed files in the repository and prompt the user to confirm the changes. Ignore uncommitted changes in .github and .vscode folders. + - If the user confirms: + - Prompt the user to commit the changes: + - Run `git add ` to stage the changes. + - Run `git commit -m ""` to commit the changes. + - Push the changes to the GitHub remote, ensuring the branch name is not "main." + - Run `git push -u origin ` to push the changes. + - If the push fails due to authentication, prompt the user to run `gh auth login` and retry the push command. + - If the user does not confirm, prompt them to fix the changes and re-run validation. -Please use Python 3.9 for compatibility, and refer to the Azure SDK design guidelines (https://azure.github.io/azure-sdk/python_design.html) for any implementation decisions. +### Step 6: Manage Pull Requests +- Check if a pull request exists for the current branch: + - If a pull request exists, inform the user and display its details. + - If no pull request exists: + - Ensure the current branch name is not "main." If it is, prompt the user to create a new branch using `git checkout -b `. + - Push the changes to the remote branch. If the branch does not exist on GitHub, create it and push the changes. + - Generate a title and description for the pull request based on the changes. Prompt the user to confirm or edit them. + - Prompt the user to select the target branch for the pull request, defaulting to "main." + - Create the pull request in DRAFT mode with the specified project, target branch, title, and description. + - Retrieve and display the pull request summary, including its status, checks, and comments. Highlight any action items. + - Return the link to the pull request for the user to review. +### Step 7: Finalize the Process + - Prompt the user to review the pull request and make any necessary changes. + - If the user is satisfied with the pull request guide them to go back to the TypeSpec project and make any necessary changes. # Pylint diff --git a/.vscode/mcp.json b/.vscode/mcp.json index 74c6150f62cf..d30aeab71c55 100644 --- a/.vscode/mcp.json +++ b/.vscode/mcp.json @@ -29,19 +29,5 @@ "main.py" ] }, - "github": { - "command": "docker", - "args": [ - "run", - "-i", - "--rm", - "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN", - "ghcr.io/github/github-mcp-server" - ], - "env": { - "GITHUB_PERSONAL_ACCESS_TOKEN": "${input:gh_token}" - } - } } } \ No newline at end of file diff --git a/tools/mcp/typespec-story/main.py b/tools/mcp/typespec-story/main.py index 04c695ab2341..7e985b36b58c 100644 --- a/tools/mcp/typespec-story/main.py +++ b/tools/mcp/typespec-story/main.py @@ -129,7 +129,7 @@ def run_typespec_cli_command(command: str, args: Dict[str, Any], root_dir: Optio # Register tools for each TypeSpec client generator CLI command @mcp.tool("init") def init_tool(tsp_config_url: str) -> Dict[str, Any]: - """Initialize a typespec client library directory given the url. + """Initializes and generates a typespec client library directory given the url. Args: tsp_config_url: The URL to the tspconfig.yaml file. @@ -153,7 +153,7 @@ def init_tool(tsp_config_url: str) -> Dict[str, Any]: @mcp.tool("init_local") def init_local_tool(tsp_config_path: str) -> Dict[str, Any]: - """Initialize a typespec client library directory from a local azure-rest-api-specs repo. + """Initializes and generates a typespec client library directory from a local azure-rest-api-specs repo. Args: tsp_config_path: The path to the local tspconfig.yaml file. @@ -173,7 +173,7 @@ def init_local_tool(tsp_config_path: str) -> Dict[str, Any]: @mcp.tool("generate") def generate_tool(project_dir: Optional[str] = None) -> Dict[str, Any]: - """Generate a typespec client library given the url. + """Generates a typespec client library given the url. Args: project_dir: The directory of the client library to be generated. @@ -192,7 +192,7 @@ def generate_tool(project_dir: Optional[str] = None) -> Dict[str, Any]: @mcp.tool("update") def update_tool(project_dir: Optional[str] = None) -> Dict[str, Any]: - """Update a typespec client library. + """Updates and generates a typespec client library. This command looks for a tsp-location.yaml file in the current directory to sync a TypeSpec project and generate a client library. It calls sync and generate commands internally. @@ -216,9 +216,9 @@ def update_tool(project_dir: Optional[str] = None) -> Dict[str, Any]: @mcp.tool("sync") def sync_tool(project_dir: Optional[str] = None) -> Dict[str, Any]: """Sync a typespec client library from the remote repository. - This command looks for a tsp-location.yaml file to get the project details and sync them to a temporary directory. - + A generate or update command is then needed to generate the client library. + Args: project_dir: The root directory where the client library will be synced. @@ -238,6 +238,9 @@ def sync_tool(project_dir: Optional[str] = None) -> Dict[str, Any]: @mcp.tool("sync_local") def sync_local_tool(local_spec_repo: str, project_dir: Optional[str] = None) -> Dict[str, Any]: """Sync a typespec client library from a local repository. + + This command looks for a tsp-location.yaml file to get the project details and sync them to a temporary directory. + A generate or update command is then needed to generate the client library. Args: local_spec_repo: The path to the local azure-rest-api-specs repository. diff --git a/tools/mcp/validation/main.py b/tools/mcp/validation/main.py index 339d6785747a..e820c604b8ca 100644 --- a/tools/mcp/validation/main.py +++ b/tools/mcp/validation/main.py @@ -11,6 +11,11 @@ handler = logging.StreamHandler(sys.stderr) logger.addHandler(handler) +# Log the Python executable and environment information +logger.info(f"Running with Python executable: {sys.executable}") +logger.info(f"Virtual environment path: {os.environ.get('VIRTUAL_ENV', 'Not running in a virtual environment')}") +logger.info(f"Working directory: {os.getcwd()}") + _REPO_ROOT = Path(__file__).parents[3] _TOX_INI_PATH = os.path.abspath(_REPO_ROOT / "eng" / "tox" / "tox.ini") @@ -19,37 +24,35 @@ def run_command(command: List[str], cwd: Optional[str] = None) -> Dict[str, Any]: """Run a command and return the result.""" + if os.name == "nt": # Windows + command = ["cmd.exe", "/C"] + command logger.info(f"Running command: {' '.join(command)}") + try: if not cwd: - cwd = os.getcwd() + cwd = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../..")) logger.info(f"Using current working directory: {cwd}") result = subprocess.run( command, - check=True, capture_output=True, text=True, cwd=cwd ) + logger.info(f"Command output: {result}") + return { "success": True, "stdout": result.stdout, "stderr": result.stderr, "code": result.returncode } - except subprocess.CalledProcessError as e: - return { - "success": False, - "stdout": e.stdout, - "stderr": e.stderr, - "code": e.returncode, - "error": str(e) - } except Exception as e: logger.error(f"Error running command: {e}") return { "success": False, - "error": str(e) + "stdout": "", + "stderr": str(e), + "code": 1, } @mcp.tool("verify_setup") @@ -80,11 +83,10 @@ def verify_installation(command: List[str], name: str) -> Dict[str, Any]: } # Check if tox is installed - if venv: - logger.info(f"Using virtual environment: {venv}") - tox_command = [os.path.join(venv, "bin", "tox"), "--version"] if os.name != "nt" else [os.path.join(venv, "Scripts", "tox.exe"), "--version", "-c", _TOX_INI_PATH, "--root", str(_REPO_ROOT)] - else: - tox_command = ["tox", "--version", "-c", _TOX_INI_PATH] + # Check if tox is installed + # Use normalized path with proper separators for Windows compatibility + tox_ini_path = str(_TOX_INI_PATH).replace('\\', '\\\\') if os.name == 'nt' else _TOX_INI_PATH + tox_command = ["tox", "--version", "-c", tox_ini_path] results["tox"] = verify_installation(tox_command, "tox") From 8eac64a52dbef9a8508dc9e79d85808df6850970 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Wed, 21 May 2025 11:43:11 -0700 Subject: [PATCH 2/7] wording of mcp server --- tools/mcp/typespec-story/main.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/mcp/typespec-story/main.py b/tools/mcp/typespec-story/main.py index 7e985b36b58c..431423699838 100644 --- a/tools/mcp/typespec-story/main.py +++ b/tools/mcp/typespec-story/main.py @@ -153,7 +153,11 @@ def init_tool(tsp_config_url: str) -> Dict[str, Any]: @mcp.tool("init_local") def init_local_tool(tsp_config_path: str) -> Dict[str, Any]: - """Initializes and generates a typespec client library directory from a local azure-rest-api-specs repo. + """Initializes and subsequently generates a typespec client library directory from a local azure-rest-api-specs repo. + + This command is used to generate a client library from a local azure-rest-api-specs repository. No additional + commands are needed to generate the client library. + Args: tsp_config_path: The path to the local tspconfig.yaml file. From 57162c5712f4a3dd77810685060cdd48bb55bce8 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Wed, 21 May 2025 11:48:29 -0700 Subject: [PATCH 3/7] this? --- .github/copilot-instructions.md | 137 +------------------------------- 1 file changed, 2 insertions(+), 135 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 796041a4b5e6..dba953ca2f30 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -52,143 +52,10 @@ are no TypeSpec project paths in the context, ask the user for the path to the t - If there are any issues that cannot be fixed, please ask the user to fix them and then come back to proceed with the next step. ### Step 4: Post-Processing of the SDK -- Create a CHANGELOG.md entry for the changes made. If there is no CHANGELOG.md file, create one in the root directory of the package. -The CHANGELOG entry should look like: - -```markdown -# Release History - -## 1.0.0 (YYYY-MM-DD) - -### Features Added - - Added a new feature to do X. - -### Breaking Changes - - Changed the way Y is done, which may break existing code that relies on the old behavior. - -### Bugs Fixed - - Fixed a bug that caused Z to not work as expected. - -### Other Changes - - Updated the documentation to reflect the new changes. - - Refactored the code to improve readability and maintainability. -``` - -### Step 5: Commit and Push the Changes -- Display the list of changed files in the repository and prompt the user to confirm the changes. Ignore uncommitted changes in .github and .vscode folders. - - If the user confirms: - - Prompt the user to commit the changes: - - Run `git add ` to stage the changes. - - Run `git commit -m ""` to commit the changes. - - Push the changes to the GitHub remote, ensuring the branch name is not "main." - - Run `git push -u origin ` to push the changes. - - If the push fails due to authentication, prompt the user to run `gh auth login` and retry the push command. - - If the user does not confirm, prompt them to fix the changes and re-run validation. - -### Step 6: Manage Pull Requests -- Check if a pull request exists for the current branch: - - If a pull request exists, inform the user and display its details. - - If no pull request exists: - - Ensure the current branch name is not "main." If it is, prompt the user to create a new branch using `git checkout -b `. - - Push the changes to the remote branch. If the branch does not exist on GitHub, create it and push the changes. - - Generate a title and description for the pull request based on the changes. Prompt the user to confirm or edit them. - - Prompt the user to select the target branch for the pull request, defaulting to "main." - - Create the pull request in DRAFT mode with the specified project, target branch, title, and description. - - Retrieve and display the pull request summary, including its status, checks, and comments. Highlight any action items. - - Return the link to the pull request for the user to review. - -### Step 7: Finalize the Process - - Prompt the user to review the pull request and make any necessary changes. - - If the user is satisfied with the pull request guide them to go back to the TypeSpec project and make any necessary changes. - - -# Pylint - -## Running Pylint -- When asked how to run pylint, or given a command to run pylint, DO check [this website](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/pylint_checking.md) and guide the user based on the information you find there. -- Do run pylint using the command `tox -e pylint --c --root .` if the user is working on a specific file. The path to the tox.ini file by default is `azure-sdk-for-python/eng/tox/tox.ini` -- For formatting the tox command DO check [this website](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/tests.md#tox) and guide the user based on the information you find there. - - - -## Fixing Pylint Warnings - -### Dos and Don'ts -- DO use the table in https://github.com/Azure/azure-sdk-tools/blob/main/tools/pylint-extensions/azure-pylint-guidelines-checker/README.md and the code examples as a guide on how to fix each rule. -- DO refer to the pylint documentation: https://pylint.readthedocs.io/en/stable/user_guide/checkers/features.html. - - -- DO NOT solve a pylint warning if you are not 100% confident about the answer. If you think your approach might not be the best, stop trying to fix the warning and leave it as is. -- DO NOT create a new file when solving a pylint error, all solutions must remain in the current file. -- DO NOT import a module or modules that do not exist to solve a pylint warning. -- DO NOT add new dependencies or imports to the project to solve a pylint warning. -- DO NOT make larger changes where a smaller change would fix the issue. -- DO NOT change the code style or formatting of the code unless it is necessary to fix a pylint warning. -- DO NOT delete code or files unless it is necessary to fix a warning. - - -# MyPy - -## Running MyPy and Fixing MyPy Warnings -- When asked how to run mypy, or given a command to run mypy, DO check [this website](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/tests.md#tox) and guide the user based on the information you find there. -- Do check this website on guidance on how to best fix MyPy issues [website link](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/static_type_checking_cheat_sheet.md) -- DO use a python 3.9 environment that is compatible with the code you are working on. If you are not sure, please ask the user for the python version they are using. # Copilot Instructions - - -# General Repository Guidelines -- DO check this [website](https://azure.github.io/azure-sdk/python_design.html), and link to pages found there, if possible, when asked about guidelines, or guidance on how to write SDKs. The general guidelines for SDK in this repo are defined there. -- DO ensure folks have the appropriate setup when working with this repository. Use the verify_setup tool in the azure-sdk-validation server. - - -# Generating an SDK From TypeSpec - -## Agent Context -- Check if there are any TypeSpec project paths in the context. If there are, use those paths to locally generate the SDK from the tsp-config.yaml file. If there -are no TypeSpec project paths in the context, ask the user for the path to the tsp-config.yaml file. If the user does not have a path, ask them to provide one. - -## Prerequisites -- The user should have a GitHub account and be logged in to GitHub using the GitHub CLI `gh auth login`. -- The user should have a GitHub Personal Access Token (PAT) with the `repo` scope. - -## Basic Rules: -### When running tsp-client commands: -- If syncing from a local repo, do not grab a commit hash. -- Do not manually create directories. The command will create the directories for you. -- If asked to sync or generate `package-name` we need to find the path to the package's tsp-location.yaml - in the azure-sdk-for-python repo and run the command in the same directory. -- If provided a url to a tspconfig.yaml ensure it has the most recent commit hash of the tspconfig.yaml file - instead of a branch name like `main`. If the url does not have a commit hash, use the GitHub API to get the most recent commit hash of the tspconfig.yaml file. - If you are unable to do this, ask the user to provide the correct url. - `curl -s "https://api.github.com/repos/Azure/azure-rest-api-specs/commits?path=,path to tspconfig.yaml>&per_page=1"` -- Ensure that node, python, tox, and the required dependencies are installed in your environment (@azure-tools/typespec-client-generator-cli) - +- Create a CHANGELOG.md entry for the changes made. If there is no CHANGELOG.md file, create one in the root directory of the package. If the package version is not correct, update it in _version.py and the CHANGELOG entry. +- Confirm that the package version in the most recent CHANGELOG entry is correct based on the API spec version and the last released package version. -## Steps to Generate: - -### Step 1: Validate the correct environment is set up -- Check if the user has the correct environment set up. If not, guide them to set it up. -- Using the `verify_setup` tool in the azure-sdk-validation server is a good way to do this. - -### Step 2: Run the correct tsp-client command(s): -- The typspec-python mcp server tools should be used to run the commands. -- If any of the commands fail, check the error message and guide the user to fix the issue. - - If a command fails due to a TypeSpec error, direct the user back to the TypeSpec to fix the error. -- If the user is generating a new package, ensure that the package name is valid and follows the naming conventions for Python packages. - -### Step 3: Validate the generated SDK and Fix the issues - - Installing the newly generated package and its dev_requirements in a .venv and installing tox. - - Use the tox mcp tool from the azure-sdk-validation server to run the following validations when possible: - - Running pylint validation using tox: `tox -e pylint -c [path to tox.ini] --root .` - - Running mypy type checking using tox: `tox -e mypy -c [path to tox.ini] --root .` - - Running pyright validation using tox: `tox -e pyright -c [path to tox.ini] --root .` - - Running verifytypes validation using tox: `tox -e verifytypes -c [path to tox.ini] --root .` - - Fix issues found during validation. - - If there are any issues that cannot be fixed, please ask the user to fix them and then come back to proceed with the next step. - -### Step 4: Post-Processing of the SDK -- Create a CHANGELOG.md entry for the changes made. If there is no CHANGELOG.md file, create one in the root directory of the package. The CHANGELOG entry should look like: - ```markdown # Release History From 3b114dff4bd052324245fe1a1bd2fb24487e1984 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Wed, 21 May 2025 13:31:04 -0700 Subject: [PATCH 4/7] update tox --- tools/mcp/validation/main.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/mcp/validation/main.py b/tools/mcp/validation/main.py index e820c604b8ca..e0d697f072fb 100644 --- a/tools/mcp/validation/main.py +++ b/tools/mcp/validation/main.py @@ -105,8 +105,7 @@ def tox_tool(package_path: str, environment: Optional[str] = None, config_file: command = ["tox", "run"] if environment: command.extend(["-e", environment]) - if config_file: - command.extend(["-c", config_file or _TOX_INI_PATH]) + command.extend(["-c", config_file if config_file is not None else _TOX_INI_PATH]) command.extend(["--root", package_path]) return run_command(command, cwd=package_path) From bf3935e21b73e6c19c695e481d6dcc48506cf8a4 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Wed, 21 May 2025 15:12:42 -0700 Subject: [PATCH 5/7] stdin issue with tox --- tools/mcp/validation/main.py | 120 ++++++++++++++++++++++++++++------- 1 file changed, 97 insertions(+), 23 deletions(-) diff --git a/tools/mcp/validation/main.py b/tools/mcp/validation/main.py index e0d697f072fb..c2fd0e262bea 100644 --- a/tools/mcp/validation/main.py +++ b/tools/mcp/validation/main.py @@ -24,28 +24,68 @@ def run_command(command: List[str], cwd: Optional[str] = None) -> Dict[str, Any]: """Run a command and return the result.""" - if os.name == "nt": # Windows - command = ["cmd.exe", "/C"] + command logger.info(f"Running command: {' '.join(command)}") + logger.info(f"Virtual environment path: {os.environ.get('VIRTUAL_ENV', 'Not running in a virtual environment')}") try: if not cwd: cwd = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../..")) logger.info(f"Using current working directory: {cwd}") - result = subprocess.run( - command, - capture_output=True, - text=True, - cwd=cwd - ) - logger.info(f"Command output: {result}") + + # On Windows, use shell=True for better command handling + use_cmd = os.name == "nt" + + if use_cmd and isinstance(command, list): + # Convert list to single command string for Windows shell + command = ["cmd.exe", "/c"] + command + cmd_str = " ".join(command) + logger.info(f"Running command in windows: {cmd_str}") + + with open("output.log", "w") as log_file: + process = subprocess.Popen( + cmd_str, + stdout=log_file, + stderr=log_file, + stdin=subprocess.DEVNULL, # Explicitly close stdin + text=True + ) + process.wait() # Wait for it to complete + # Read the result from the file + with open("output.log", "r") as log_file: + output = log_file.read() + # Simulate a subprocess.CompletedProcess-like object + class Result: + def __init__(self, stdout): + self.returncode = 0 # Assume success; you may want to parse output/log for errors + self.stdout = stdout + self.stderr = "" + result = Result(output) + else: + result = subprocess.run( + command, + capture_output=True, + text=True, + cwd=cwd, + ) + + logger.info(f"Command exit code: {result.returncode}") + logger.info(f"Command stdout: {result.stdout}") + logger.info(f"Command stderr: {result.stderr}") return { - "success": True, + "success": result.returncode == 0, "stdout": result.stdout, "stderr": result.stderr, "code": result.returncode } + except subprocess.TimeoutExpired as e: + logger.error(f"Command timed out: {e}") + return { + "success": False, + "stdout": "", + "stderr": str(e), + "code": 1, + } except Exception as e: logger.error(f"Error running command: {e}") return { @@ -64,17 +104,29 @@ def verify_setup_tool(venv: Optional[str] = None) -> Dict[str, Any]: """ def verify_installation(command: List[str], name: str) -> Dict[str, Any]: """Helper function to verify installation of a tool.""" + logger.info(f"Checking installation of {name} with command: {command}") + result = run_command(command) + if not result["success"]: - logger.error(f"{name} is not installed or not available in PATH.") + logger.error(f"{name} verification failed. Exit code: {result['code']}") + logger.error(f"stderr: {result['stderr']}") return { "success": False, - "message": f"{name} is not installed or not available in PATH." + "message": f"{name} is not installed or not available in PATH.", + "details": { + "stdout": result["stdout"], + "stderr": result["stderr"], + "exit_code": result["code"] + } } - logger.info(f"{name} version: {result['stdout'].strip()}") + + version_output = result["stdout"].strip() or "No version output" + logger.info(f"{name} version output: '{version_output}'") + return { "success": True, - "message": f"{name} is installed. Version: {result['stdout'].strip()}" + "message": f"{name} is installed. Version: {version_output}" } results = { @@ -83,13 +135,22 @@ def verify_installation(command: List[str], name: str) -> Dict[str, Any]: } # Check if tox is installed - # Check if tox is installed - # Use normalized path with proper separators for Windows compatibility - tox_ini_path = str(_TOX_INI_PATH).replace('\\', '\\\\') if os.name == 'nt' else _TOX_INI_PATH - tox_command = ["tox", "--version", "-c", tox_ini_path] - - results["tox"] = verify_installation(tox_command, "tox") - + logger.info("Checking tox installation...") + + # For Windows, try both with python -m tox and direct tox command + if os.name == "nt": + tox_command = ["tox", "--version", "-c", _TOX_INI_PATH] + results["tox"] = verify_installation(tox_command, "tox (via python -m)") + + if not results["tox"]["success"]: + # Fallback to direct tox command + direct_tox_command = ["tox", "--version"] + results["tox"] = verify_installation(direct_tox_command, "tox (direct command)") + else: + # For non-Windows systems, use the regular tox command + tox_command = ["tox", "--version"] + results["tox"] = verify_installation(tox_command, "tox") + return results @@ -102,11 +163,24 @@ def tox_tool(package_path: str, environment: Optional[str] = None, config_file: environment: Optional tox environment to run (e.g., 'pylint', 'mypy') config_file: Optional path to a tox configuration file """ - command = ["tox", "run"] + # Normalize config file path + config_path = config_file if config_file is not None else _TOX_INI_PATH + + # On Windows, use python -m tox to ensure proper execution + if os.name == "nt": + command = ["python", "-m", "tox", "run"] + else: + command = ["tox", "run"] + if environment: command.extend(["-e", environment]) - command.extend(["-c", config_file if config_file is not None else _TOX_INI_PATH]) + + command.extend(["-c", config_path]) command.extend(["--root", package_path]) + + logger.info(f"Running tox with command: {command}") + logger.info(f"Working directory: {package_path}") + return run_command(command, cwd=package_path) # Run the MCP server From d3d8d41b098fec213f1fa813978be71a069aa6ac Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Wed, 21 May 2025 15:19:05 -0700 Subject: [PATCH 6/7] try to do the same for tsp-client --- tools/mcp/typespec-story/main.py | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/tools/mcp/typespec-story/main.py b/tools/mcp/typespec-story/main.py index 431423699838..857622df137a 100644 --- a/tools/mcp/typespec-story/main.py +++ b/tools/mcp/typespec-story/main.py @@ -91,16 +91,34 @@ def run_typespec_cli_command(command: str, args: Dict[str, Any], root_dir: Optio logger.info(f"Running command: {' '.join(cli_args)}") try: - # TODO: maybe if we return and dont wait for output this will work # Run the command and capture the output if root_dir: - result = subprocess.run( - cli_args, - capture_output=True, - text=True, - cwd=root_dir, - ) + if os.name == "nt": # Windows + result = subprocess.Popen( + cli_args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.DEVNULL, # Explicitly close stdin + text=True + ) + result.wait() # Wait for it to complete + else: + result = subprocess.run( + cli_args, + capture_output=True, + text=True, + cwd=root_dir, + ) else: + if os.name == "nt": + result = subprocess.Popen( + cli_args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.DEVNULL, # Explicitly close stdin + text=True + ) + result.wait() result = subprocess.run( cli_args, capture_output=True, From a401a7762c52b9a6da8a12d9dc760d2560a350c6 Mon Sep 17 00:00:00 2001 From: Libba Lawrence Date: Wed, 21 May 2025 15:24:08 -0700 Subject: [PATCH 7/7] needs root dir --- tools/mcp/typespec-story/main.py | 3 ++- tools/mcp/validation/main.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/mcp/typespec-story/main.py b/tools/mcp/typespec-story/main.py index 857622df137a..ca6bdbdeb17c 100644 --- a/tools/mcp/typespec-story/main.py +++ b/tools/mcp/typespec-story/main.py @@ -99,7 +99,8 @@ def run_typespec_cli_command(command: str, args: Dict[str, Any], root_dir: Optio stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL, # Explicitly close stdin - text=True + text=True, + cwd=root_dir, ) result.wait() # Wait for it to complete else: diff --git a/tools/mcp/validation/main.py b/tools/mcp/validation/main.py index c2fd0e262bea..fd8732360f97 100644 --- a/tools/mcp/validation/main.py +++ b/tools/mcp/validation/main.py @@ -47,7 +47,8 @@ def run_command(command: List[str], cwd: Optional[str] = None) -> Dict[str, Any] stdout=log_file, stderr=log_file, stdin=subprocess.DEVNULL, # Explicitly close stdin - text=True + text=True, + cwd=cwd, ) process.wait() # Wait for it to complete # Read the result from the file