From eb67f32a310c7c6cd964051e88534ef6e48315d6 Mon Sep 17 00:00:00 2001 From: ZelinWang Date: Fri, 19 Sep 2025 14:15:49 +0800 Subject: [PATCH 1/2] Add status check for extension release pipeline --- .../workflows/TestTriggerExtensionRelease.yml | 10 +-- .github/workflows/TriggerExtensionRelease.yml | 61 ++++++++++++++++++- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/.github/workflows/TestTriggerExtensionRelease.yml b/.github/workflows/TestTriggerExtensionRelease.yml index d5cd7c7f42b..b1291c5398c 100644 --- a/.github/workflows/TestTriggerExtensionRelease.yml +++ b/.github/workflows/TestTriggerExtensionRelease.yml @@ -13,7 +13,7 @@ permissions: jobs: build: - name: Trigger Extension Release Pipeline + name: Test Trigger Extension Release Pipeline runs-on: ubuntu-latest steps: - name: Harden Runner @@ -26,7 +26,7 @@ jobs: client-id: ${{ secrets.ADO_SP_ClientID }} tenant-id: ${{ secrets.ADO_SP_TenantID }} allow-no-subscriptions: true - - name: Trigger ADO Pipeline and Wait for Completion + - name: Test Trigger ADO Pipeline and Wait for Completion uses: azure/cli@v2 env: ado-org: ${{secrets.ADO_ORGANIZATION}} @@ -89,7 +89,7 @@ jobs: exit 1 fi - # Wait 30 seconds before checking again - echo "Build still running... waiting 30 seconds" - sleep 30 + # Wait 60 seconds before checking again + echo "Build still running... waiting 60 seconds" + sleep 60 done diff --git a/.github/workflows/TriggerExtensionRelease.yml b/.github/workflows/TriggerExtensionRelease.yml index a452d46201d..fbd07a0c609 100644 --- a/.github/workflows/TriggerExtensionRelease.yml +++ b/.github/workflows/TriggerExtensionRelease.yml @@ -26,7 +26,7 @@ jobs: client-id: ${{ secrets.ADO_SP_ClientID }} tenant-id: ${{ secrets.ADO_SP_TenantID }} allow-no-subscriptions: true - - name: Azure CLI + - name: Trigger ADO Pipeline and Wait for Completion uses: azure/cli@v2 env: ado-org: ${{secrets.ADO_ORGANIZATION}} @@ -35,4 +35,61 @@ jobs: commit-id: ${{ github.sha }} with: inlineScript: | - az pipelines build queue --definition-id ${{ env.ado-pipeline-id }} --organization ${{ env.ado-org }} --project ${{ env.ado-project }} --variables commit_id=${{ env.commit-id }} + # Trigger the pipeline and capture the build ID + echo "Triggering ADO pipeline..." + BUILD_RESULT=$(az pipelines build queue \ + --definition-id ${{ env.ado-pipeline-id }} \ + --organization ${{ env.ado-org }} \ + --project ${{ env.ado-project }} \ + --variables commit_id=${{ env.commit-id }} \ + --output json) + + BUILD_ID=$(echo $BUILD_RESULT | jq -r '.id') + echo "Pipeline triggered with Build ID: $BUILD_ID" + + if [ "$BUILD_ID" = "null" ] || [ -z "$BUILD_ID" ]; then + echo "Failed to get build ID from pipeline trigger" + exit 1 + fi + + # Wait for the build to complete + echo "Waiting for build $BUILD_ID to complete..." + while true; do + BUILD_JSON=$(az pipelines build show \ + --id $BUILD_ID \ + --organization ${{ env.ado-org }} \ + --project ${{ env.ado-project }} \ + --output json) + + BUILD_STATUS=$(echo "$BUILD_JSON" | jq -r '.status') + BUILD_RESULT_STATUS=$(echo "$BUILD_JSON" | jq -r '.result // "none"') + + echo "Current status: $BUILD_STATUS, Result: $BUILD_RESULT_STATUS" + + # Check if build is completed + if [ "$BUILD_STATUS" = "completed" ]; then + echo "Build completed with result: $BUILD_RESULT_STATUS" + + # Check if the build was successful + if [ "$BUILD_RESULT_STATUS" = "succeeded" ]; then + echo "✅ ADO pipeline build succeeded!" + exit 0 + elif [ "$BUILD_RESULT_STATUS" = "partiallySucceeded" ]; then + echo "⚠️ ADO pipeline build partially succeeded" + exit 1 + else + echo "❌ ADO pipeline build failed with result: $BUILD_RESULT_STATUS" + exit 1 + fi + fi + + # Check for other terminal states + if [ "$BUILD_STATUS" = "cancelling" ] || [ "$BUILD_STATUS" = "cancelled" ]; then + echo "❌ ADO pipeline build was cancelled" + exit 1 + fi + + # Wait 60 seconds before checking again + echo "Build still running... waiting 60 seconds" + sleep 60 + done From c66b97b76e0551f9950ca204afd8a7bcbadd72f6 Mon Sep 17 00:00:00 2001 From: ZelinWang Date: Fri, 19 Sep 2025 14:36:17 +0800 Subject: [PATCH 2/2] remove find_extension_upgraded.py --- scripts/ci/find_extension_upgraded.py | 141 -------------------------- 1 file changed, 141 deletions(-) delete mode 100644 scripts/ci/find_extension_upgraded.py diff --git a/scripts/ci/find_extension_upgraded.py b/scripts/ci/find_extension_upgraded.py deleted file mode 100644 index 3fc8112b074..00000000000 --- a/scripts/ci/find_extension_upgraded.py +++ /dev/null @@ -1,141 +0,0 @@ -# -------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# -------------------------------------------------------------------------------------------- -import os -import sys -import json -from subprocess import check_output -from pkg_resources import parse_version - -def separator_line(): - print('-' * 100) - -class AzdevExtensionHelper: - def __init__(self, extension_name): - self.name = extension_name - - def _get_metadata_flags(self, setup_py_dir): - for root, _, files in os.walk(setup_py_dir): - if 'azext_metadata.json' in files: - metadata_path = os.path.join(root, 'azext_metadata.json') - with open(metadata_path, 'r') as f: - metadata = json.load(f) - is_experimental = metadata.get('azext.isExperimental', False) - is_preview = metadata.get('azext.isPreview', False) - return is_experimental, is_preview - return False, False - - def _adjust_version_for_preview(self, version, setup_py_dir): - is_experimental, is_preview = self._get_metadata_flags(setup_py_dir) - if is_experimental or is_preview: - version_parts = version.split('.') - if all(part.isdigit() for part in version_parts): - version = f"{version}b1" - return version - - def is_version_upgrade(self): - with open('src/index.json') as fd: - current_extensions = json.loads(fd.read()).get("extensions") - - setup_py = f'src/{self.name}/setup.py' - if not os.path.isfile(setup_py): - print('no setup.py') - return False - - setup_py_dir = os.path.dirname(setup_py) - cmd = f'{sys.executable} setup.py --name' - self.name = check_output(cmd, shell=True, cwd=setup_py_dir).decode('utf-8').strip() - self.name = self.name.replace('_', '-') - - metadata = current_extensions.get(self.name, None) - if metadata is None: # for new added extension - return True - - current_max_entry = max(metadata, key=lambda e: parse_version(e['metadata']['version'])) - current_max_version = current_max_entry['metadata']['version'] - current_max_version = self._adjust_version_for_preview(current_max_version, setup_py_dir) - print(f'current max version is {current_max_version}') - - cmd = f'{sys.executable} setup.py --version' - modified_version = check_output(cmd, shell=True, cwd=setup_py_dir).decode('utf-8').strip() - print(f'modified version is {modified_version}') - - if parse_version(current_max_version) > parse_version(modified_version): - err = f'version downgrade is not allowed in extension {setup_py}. [{current_max_version} -> {modified_version}]' - raise Exception(err) - - if parse_version(current_max_version) == parse_version(modified_version): - return False - - return True - -def find_modified_files_against_master_branch(): - """ - Deleted files don't count in diff - """ - cmd = 'git --no-pager diff --diff-filter=ACMRT --name-only HEAD~1 -- src/' - files = check_output(cmd.split()).decode('utf-8').split('\n') - separator_line() - print('modified files:') - for f in files: - print(f) - separator_line() - return [f for f in files if len(f) > 0] - -def contain_extension_code(files): - with open('src/index.json', 'r') as fd: - current_extensions = json.loads(fd.read()).get("extensions") - current_extension_homes = set(f'src/{name}' for name in current_extensions) - - for file in files: - if any([file.startswith(prefix) for prefix in current_extension_homes]): - return True - - # for new added extensions or modules that src folder name does not match its wheel package name - for file in files: - if 'src/' in file and os.path.isfile(file) and os.path.isdir(os.path.dirname(file)): - new_extension_home = os.path.dirname(file) - new_extension_home = os.path.join(*new_extension_home.split('/')[:2]) - if os.path.isfile(os.path.join(new_extension_home, 'setup.py')): - return True - return False - -def main(): - modified_files = find_modified_files_against_master_branch() - if 'src/index.json' in modified_files: - modified_files.remove('src/index.json') - - # check setup.py - for f in modified_files: - if f.endswith('setup.py'): - break - else: - separator_line() - print('no setup.py is modified, no need to publish') - separator_line() - return - - # check source code - if not contain_extension_code(modified_files): - separator_line() - print('no extension source code is modified, no need to publish') - separator_line() - return - - extension_names = set() - for f in modified_files: - src, name, *_ = f.split('/') - if os.path.isdir(os.path.join(src, name)): - extension_names.add(name) - - for name in extension_names: - azdev_extension = AzdevExtensionHelper(name) - if azdev_extension.is_version_upgrade() is False: - print(f'extension [{name}] is not upgrade, no need to help publish') - continue - with open('./upgrade_extensions.txt', 'a') as fd: - fd.write(name + '\n') - -if __name__ == '__main__': - main()