From e2c17fca6830c2dd0dc38e45d0d527be0b399d67 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Mon, 10 Aug 2026 10:19:46 +1000 Subject: [PATCH 01/24] Add Linux tarball support for Homebrew Cask --- .azure-pipelines/macos-standalone-release.yml | 7 ++ .../templates/linux/linux-build-jobs.yml | 92 +++++++++++++++++++ .../macos/macos-cask-generation-and-tests.yml | 29 ++++-- .../templates/macos/macos-publish-jobs.yml | 40 +++++++- scripts/release/macos/build_binary_tar_gz.py | 55 ++++++++--- scripts/release/macos/cask_generate.py | 30 +++--- .../release/macos/templates/az_launcher.sh.in | 10 +- .../release/macos/templates/azure-cli.rb.in | 9 +- 8 files changed, 232 insertions(+), 40 deletions(-) create mode 100644 .azure-pipelines/templates/linux/linux-build-jobs.yml diff --git a/.azure-pipelines/macos-standalone-release.yml b/.azure-pipelines/macos-standalone-release.yml index 31aee2ba575..1b663db575a 100644 --- a/.azure-pipelines/macos-standalone-release.yml +++ b/.azure-pipelines/macos-standalone-release.yml @@ -115,6 +115,12 @@ jobs: MacosArm64Image: ${{ variables.macos_arm64_pool }} MacosIntelImage: ${{ variables.macos_intel_pool }} +- template: templates/linux/linux-build-jobs.yml + parameters: + PythonVersion: ${{ parameters.PythonVersion }} + LinuxArm64Pool: ${{ variables.ubuntu_arm64_pool }} + LinuxX64Pool: ${{ variables.ubuntu_pool }} + # Jobs included: # - BuildMacOSCli (matrix: ARM64 + Intel) # - VerifyMacOSCli (matrix: ARM64 + Intel) @@ -156,6 +162,7 @@ jobs: Debug: ${{ parameters.Debug }} dependsOn: - CreateFinalTarball + - BuildLinuxCli # Jobs included: # - TestTempTapCask (matrix: ARM64 + Intel) - tests cask with local file:// URLs diff --git a/.azure-pipelines/templates/linux/linux-build-jobs.yml b/.azure-pipelines/templates/linux/linux-build-jobs.yml new file mode 100644 index 00000000000..c46bc7fea62 --- /dev/null +++ b/.azure-pipelines/templates/linux/linux-build-jobs.yml @@ -0,0 +1,92 @@ +# Linux tarball build jobs for the Homebrew Cask release. + +parameters: + - name: PythonVersion + type: string + default: '3.14' + - name: LinuxArm64Pool + type: string + default: 'pool-ubuntu-latest-arm64' + - name: LinuxX64Pool + type: string + default: 'pool-ubuntu-2204' + - name: condition + type: string + default: 'succeeded()' + - name: dependsOn + type: object + default: [] + +jobs: +- job: BuildLinuxCli + displayName: 'Linux | Build and verify CLI' + condition: ${{ parameters.condition }} + dependsOn: ${{ parameters.dependsOn }} + strategy: + matrix: + ARM64: + Architecture: 'arm64' + PoolName: ${{ parameters.LinuxArm64Pool }} + X64: + Architecture: 'x86_64' + PoolName: ${{ parameters.LinuxX64Pool }} + pool: + name: $(PoolName) + timeoutInMinutes: 60 + + steps: + - checkout: self + fetchDepth: 1 + + - bash: | + set -euo pipefail + + brew install python@${{ parameters.PythonVersion }} + PYTHON_PATH="$(brew --prefix python@${{ parameters.PythonVersion }})/libexec/bin/python" + "$PYTHON_PATH" --version + echo "##vso[task.setvariable variable=PythonPath]$PYTHON_PATH" + displayName: 'Install Homebrew Python' + + - bash: | + set -euo pipefail + + ARCH="$(Architecture)" + OUTPUT_DIR="$(Build.ArtifactStagingDirectory)/cli-build" + "$(PythonPath)" scripts/release/macos/build_binary_tar_gz.py \ + --platform-tag "linux-$ARCH" \ + --output-dir "$OUTPUT_DIR" + + SOURCE_TARBALL=$(find "$OUTPUT_DIR" -name "azure-cli-*-linux-$ARCH-nopython.tar.gz" -print -quit) + VERSION=$(basename "$SOURCE_TARBALL" | sed -E "s/^azure-cli-(.*)-linux-$ARCH-nopython\.tar\.gz$/\1/") + RELEASE_TARBALL="$OUTPUT_DIR/azure-cli-$VERSION-linux-$ARCH.tar.gz" + cp "$SOURCE_TARBALL" "$RELEASE_TARBALL" + sha256sum "$RELEASE_TARBALL" > "$RELEASE_TARBALL.sha256" + rm "$SOURCE_TARBALL" "$SOURCE_TARBALL.sha256" + + echo "Release tarball: $RELEASE_TARBALL" + cat "$RELEASE_TARBALL.sha256" + displayName: 'Build release tarball' + env: + PYTHON_MAJOR_MINOR: ${{ parameters.PythonVersion }} + + - bash: | + set -euo pipefail + + ARCH="$(Architecture)" + TARBALL=$(find "$(Build.ArtifactStagingDirectory)/cli-build" -name "azure-cli-*-linux-$ARCH.tar.gz" -print -quit) + EXTRACT_DIR=$(mktemp -d) + trap 'rm -rf "$EXTRACT_DIR"' EXIT + + tar -xzf "$TARBALL" -C "$EXTRACT_DIR" + export AZ_PYTHON="$(PythonPath)" + "$EXTRACT_DIR/bin/az" version + displayName: 'Smoke test release tarball' + + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: 'Generate SBOM' + inputs: + BuildDropPath: $(Build.ArtifactStagingDirectory)/cli-build + + - publish: $(Build.ArtifactStagingDirectory)/cli-build + artifact: 'linux-cli-final-$(Architecture)' + displayName: 'Publish Linux CLI ($(Architecture))' \ No newline at end of file diff --git a/.azure-pipelines/templates/macos/macos-cask-generation-and-tests.yml b/.azure-pipelines/templates/macos/macos-cask-generation-and-tests.yml index ce34c4f3ff9..e70924aabf8 100644 --- a/.azure-pipelines/templates/macos/macos-cask-generation-and-tests.yml +++ b/.azure-pipelines/templates/macos/macos-cask-generation-and-tests.yml @@ -13,7 +13,8 @@ # - dependsOn: Jobs this depends on (typically CreateFinalTarball) # # Requires: -# - Artifacts: macos-cli-signed-notarized-arm64, macos-cli-signed-notarized-x86_64 +# - Artifacts: macos-cli-signed-notarized-arm64, macos-cli-signed-notarized-x86_64, +# linux-cli-final-arm64, linux-cli-final-x86_64 # # Produces: # - Artifact: macos-cask-definition (contains azure-cli.rb with GitHub URLs) @@ -72,6 +73,14 @@ jobs: artifact: 'macos-cli-signed-notarized-x86_64' displayName: 'Download signed tarball (Intel)' + - download: current + artifact: 'linux-cli-final-arm64' + displayName: 'Download Linux tarball (ARM64)' + + - download: current + artifact: 'linux-cli-final-x86_64' + displayName: 'Download Linux tarball (x86_64)' + - bash: | echo "=== Environment Info ($(Architecture)) ===" echo "HOMEBREW_CASK_OPTS: $HOMEBREW_CASK_OPTS" @@ -101,18 +110,22 @@ jobs: # Get tarballs ARM64_TARBALL=$(find $(Pipeline.Workspace)/macos-cli-signed-notarized-arm64 -name "azure-cli-*-macos-arm64.tar.gz" | head -1) X86_64_TARBALL=$(find $(Pipeline.Workspace)/macos-cli-signed-notarized-x86_64 -name "azure-cli-*-macos-x86_64.tar.gz" | head -1) + ARM64_LINUX_TARBALL=$(find $(Pipeline.Workspace)/linux-cli-final-arm64 -name "azure-cli-*-linux-arm64.tar.gz" | head -1) + X86_64_LINUX_TARBALL=$(find $(Pipeline.Workspace)/linux-cli-final-x86_64 -name "azure-cli-*-linux-x86_64.tar.gz" | head -1) VERSION=$(basename "$ARM64_TARBALL" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+([a-z0-9]+)?' | head -1) - ARM64_SHA256=$(cat ${ARM64_TARBALL}.sha256 | cut -d' ' -f1) - X86_64_SHA256=$(cat ${X86_64_TARBALL}.sha256 | cut -d' ' -f1) + ARM64_MACOS_SHA256=$(cat ${ARM64_TARBALL}.sha256 | cut -d' ' -f1) + X86_64_MACOS_SHA256=$(cat ${X86_64_TARBALL}.sha256 | cut -d' ' -f1) + ARM64_LINUX_SHA256=$(cat ${ARM64_LINUX_TARBALL}.sha256 | cut -d' ' -f1) + X86_64_LINUX_SHA256=$(cat ${X86_64_LINUX_TARBALL}.sha256 | cut -d' ' -f1) echo "=== Generating Cask using template ===" echo "Version: $VERSION" echo "ARM64 tarball: $ARM64_TARBALL" - echo "ARM64 SHA256: $ARM64_SHA256" + echo "macOS ARM64 SHA256: $ARM64_MACOS_SHA256" echo "x86_64 tarball: $X86_64_TARBALL" - echo "x86_64 SHA256: $X86_64_SHA256" + echo "macOS x86_64 SHA256: $X86_64_MACOS_SHA256" # Resolve repo root REPO_NAME="$(Build.Repository.Name)" @@ -137,8 +150,10 @@ jobs: mkdir -p $(Pipeline.Workspace)/macos-cask-definition python3 "$REPO_ROOT/scripts/release/macos/cask_generate.py" \ --version "$VERSION" \ - --arm64-sha "$ARM64_SHA256" \ - --x86-64-sha "$X86_64_SHA256" \ + --arm64-macos-sha "$ARM64_MACOS_SHA256" \ + --x86-64-macos-sha "$X86_64_MACOS_SHA256" \ + --arm64-linux-sha "$ARM64_LINUX_SHA256" \ + --x86-64-linux-sha "$X86_64_LINUX_SHA256" \ --github-repo "${{ parameters.GitHubRepo }}" \ --python-version "${{ parameters.PythonVersion }}" \ --template "$REPO_ROOT/scripts/release/macos/templates/azure-cli.rb.in" \ diff --git a/.azure-pipelines/templates/macos/macos-publish-jobs.yml b/.azure-pipelines/templates/macos/macos-publish-jobs.yml index 6640ef48283..4d313cbfecf 100644 --- a/.azure-pipelines/templates/macos/macos-publish-jobs.yml +++ b/.azure-pipelines/templates/macos/macos-publish-jobs.yml @@ -16,7 +16,8 @@ # - dependsOn: Jobs this depends on (typically test jobs) # # Requires: -# - Artifacts: macos-cli-signed-notarized-arm64, macos-cli-signed-notarized-x86_64, macos-cask-definition +# - Artifacts: macos-cli-signed-notarized-arm64, macos-cli-signed-notarized-x86_64, +# linux-cli-final-arm64, linux-cli-final-x86_64, macos-cask-definition # - Repository resource: homebrewtap (for Homebrew cask updates) parameters: @@ -79,6 +80,14 @@ jobs: - download: current artifact: 'macos-cli-signed-notarized-x86_64' displayName: 'Download signed tarball (Intel)' + + - download: current + artifact: 'linux-cli-final-arm64' + displayName: 'Download Linux tarball (ARM64)' + + - download: current + artifact: 'linux-cli-final-x86_64' + displayName: 'Download Linux tarball (x86_64)' - task: CopyFiles@2 displayName: 'Copy ARM64 release files' @@ -99,6 +108,26 @@ jobs: azure-cli-*.tar.gz.sha256 TargetFolder: '$(Build.ArtifactStagingDirectory)/release' flattenFolders: true + + - task: CopyFiles@2 + displayName: 'Copy Linux ARM64 release files' + inputs: + SourceFolder: '$(Pipeline.Workspace)/linux-cli-final-arm64' + Contents: | + azure-cli-*.tar.gz + azure-cli-*.tar.gz.sha256 + TargetFolder: '$(Build.ArtifactStagingDirectory)/release' + flattenFolders: true + + - task: CopyFiles@2 + displayName: 'Copy Linux x86_64 release files' + inputs: + SourceFolder: '$(Pipeline.Workspace)/linux-cli-final-x86_64' + Contents: | + azure-cli-*.tar.gz + azure-cli-*.tar.gz.sha256 + TargetFolder: '$(Build.ArtifactStagingDirectory)/release' + flattenFolders: true - bash: | ARM64_TARBALL=$(find "$(Build.ArtifactStagingDirectory)/release" -name "azure-cli-*-macos-arm64.tar.gz" | head -1) @@ -131,18 +160,19 @@ jobs: target: 'main' tagSource: 'userSpecifiedTag' tag: '$(ReleaseTag)' - title: 'Azure CLI $(AzureCliVersion) - macOS (Homebrew Python)' + title: 'Azure CLI $(AzureCliVersion) - Homebrew Cask' releaseNotesSource: 'inline' releaseNotesInline: | - ## Azure CLI $(AzureCliVersion) - macOS (Homebrew Python) + ## Azure CLI $(AzureCliVersion) - Homebrew Cask ### Available Architectures - **Apple Silicon (arm64)**: azure-cli-$(AzureCliVersion)-macos-arm64.tar.gz - **Intel (x86_64)**: azure-cli-$(AzureCliVersion)-macos-x86_64.tar.gz + - **Linux ARM64**: azure-cli-$(AzureCliVersion)-linux-arm64.tar.gz + - **Linux x86_64**: azure-cli-$(AzureCliVersion)-linux-x86_64.tar.gz ### Requirements - - macOS 11+ (Big Sur or later) - - Homebrew Python ${{ parameters.PythonVersion }} + - Homebrew Python ${{ parameters.PythonVersion }} on macOS or Linux ### Installation via Homebrew Cask ```bash diff --git a/scripts/release/macos/build_binary_tar_gz.py b/scripts/release/macos/build_binary_tar_gz.py index c673d3b7754..1007351c4e5 100644 --- a/scripts/release/macos/build_binary_tar_gz.py +++ b/scripts/release/macos/build_binary_tar_gz.py @@ -15,8 +15,8 @@ Output Structure: ``` dist/binary_tar_gz/ - azure-cli-{VERSION}-macos-arm64-nopython.tar.gz - azure-cli-{VERSION}-macos-arm64-nopython.tar.gz.sha256 + azure-cli-{VERSION}-{PLATFORM_TAG}-nopython.tar.gz + azure-cli-{VERSION}-{PLATFORM_TAG}-nopython.tar.gz.sha256 ``` Archive Contents: @@ -48,8 +48,9 @@ Usage: python build_binary_tar_gz.py --help python build_binary_tar_gz.py --platform-tag macos-arm64 + python build_binary_tar_gz.py --platform-tag linux-x86_64 python build_binary_tar_gz.py --platform-tag macos-arm64 --output-dir ./dist/custom - python build_binary_tar_gz.py --platform-tag macos-arm64 --keep-temp + python build_binary_tar_gz.py --platform-tag macos-arm64 --keep-temp Requirements: - Homebrew python@x.yz installed: brew install python@x.yz @@ -61,6 +62,7 @@ import argparse import hashlib import os +import platform import shutil import subprocess import sys @@ -72,12 +74,16 @@ PROJECT_ROOT = Path(__file__).resolve().parents[3] SRC_DIR = PROJECT_ROOT / "src" AZURE_CLI_CORE_DIR = SRC_DIR / "azure-cli-core" -REQUIREMENTS_FILE = SRC_DIR / "azure-cli" / "requirements.py3.Darwin.txt" +REQUIREMENTS_FILES = { + "linux": SRC_DIR / "azure-cli" / "requirements.py3.Linux.txt", + "macos": SRC_DIR / "azure-cli" / "requirements.py3.Darwin.txt", +} # Package configuration APP_NAME = "azure-cli" CLI_EXECUTABLE_NAME = "az" TARBALL_NAME_TEMPLATE_DEFAULT = "{APP_NAME}-{VERSION}-{PLATFORM_TAG}-nopython.tar.gz" +PLATFORM_TAGS = ("linux-arm64", "linux-x86_64", "macos-arm64", "macos-x86_64") # Python version we're building for (must match Homebrew python@X.Y) # Can be overridden via PYTHON_MAJOR_MINOR env var @@ -140,8 +146,10 @@ def find_homebrew_python() -> Path: candidates = [ Path(f"/opt/homebrew/opt/python@{PYTHON_MAJOR_MINOR}/libexec/bin/python"), Path(f"/usr/local/opt/python@{PYTHON_MAJOR_MINOR}/libexec/bin/python"), + Path(f"/home/linuxbrew/.linuxbrew/opt/python@{PYTHON_MAJOR_MINOR}/libexec/bin/python"), Path(f"/opt/homebrew/bin/python{PYTHON_MAJOR_MINOR}"), Path(f"/usr/local/bin/python{PYTHON_MAJOR_MINOR}"), + Path(f"/home/linuxbrew/.linuxbrew/bin/python{PYTHON_MAJOR_MINOR}"), ] for python_path in candidates: @@ -182,12 +190,12 @@ def create_venv(python_path: Path, venv_dir: Path) -> Path: return venv_python -def install_azure_cli(venv_python: Path) -> None: +def install_azure_cli(venv_python: Path, requirements_file: Path) -> None: """Install Azure CLI components from source, then install pinned dependencies. Mirrors the run.sh approach: 1. Install all src packages with --no-deps (local source code takes precedence) - 2. Install pinned dependencies from requirements.py3.Darwin.txt + 2. Install pinned dependencies from the platform requirements file """ # Step 1: install every package found under SRC_DIR from source, without pulling # transitive deps from PyPI (--no-deps). This ensures the locally-built wheels @@ -212,12 +220,12 @@ def install_azure_cli(venv_python: Path) -> None: # resolves to the exact version recorded in the requirements file. print("\n=== Step 2: Installing pinned dependencies from requirements file ===") - if not REQUIREMENTS_FILE.exists(): - raise BuildError(f"Requirements file not found: {REQUIREMENTS_FILE}") + if not requirements_file.exists(): + raise BuildError(f"Requirements file not found: {requirements_file}") - print(f" Using: {REQUIREMENTS_FILE}") + print(f" Using: {requirements_file}") subprocess.run( - [str(venv_python), "-m", "pip", "install", "-r", str(REQUIREMENTS_FILE)], + [str(venv_python), "-m", "pip", "install", "-r", str(requirements_file)], check=True, ) @@ -428,13 +436,34 @@ def create_tarball( return tarball_path +def _platform_from_tag(platform_tag: str) -> str: + return platform_tag.split("-", maxsplit=1)[0] + + +def _validate_build_architecture(platform_tag: str) -> None: + """Reject releases whose tag does not match the builder's architecture.""" + expected_architecture = platform_tag.split("-", maxsplit=1)[1] + actual_architecture = platform.machine().lower() + aliases = { + "aarch64": "arm64", + "amd64": "x86_64", + "x64": "x86_64", + } + actual_architecture = aliases.get(actual_architecture, actual_architecture) + if actual_architecture != expected_architecture: + raise BuildError( + f"Platform tag {platform_tag} requires {expected_architecture}, " + f"but the builder is running on {actual_architecture}" + ) + + def main() -> int: """Main entry point.""" parser = argparse.ArgumentParser( description="Build Azure CLI tar.gz using Homebrew Python (no bundled Python)" ) parser.add_argument( - "--platform-tag", required=True, choices=["macos-arm64", "macos-x86_64"], help="Platform tag for the build" + "--platform-tag", required=True, choices=PLATFORM_TAGS, help="Platform tag for the build" ) parser.add_argument( "--output-dir", @@ -459,6 +488,8 @@ def main() -> int: print() try: + _validate_build_architecture(args.platform_tag) + requirements_file = REQUIREMENTS_FILES[_platform_from_tag(args.platform_tag)] version = get_cli_version() print(f"Azure CLI version: {version}") @@ -470,7 +501,7 @@ def main() -> int: install_dir = temp_path / "install" venv_python = create_venv(python_path=python_path, venv_dir=venv_dir) - install_azure_cli(venv_python=venv_python) + install_azure_cli(venv_python=venv_python, requirements_file=requirements_file) create_install_structure( venv_dir=venv_dir, diff --git a/scripts/release/macos/cask_generate.py b/scripts/release/macos/cask_generate.py index 6520ad4612f..ac6446beb44 100644 --- a/scripts/release/macos/cask_generate.py +++ b/scripts/release/macos/cask_generate.py @@ -8,20 +8,22 @@ Usage: python3 scripts/release/macos/cask_generate.py \ --version "2.x.y" \ - --arm64-sha "" \ - --x86-64-sha "" \ + --arm64-macos-sha "" \ + --x86-64-macos-sha "" \ + --arm64-linux-sha "" \ + --x86-64-linux-sha "" \ --github-repo "Azure/azure-cli" \ --template "scripts/release/macos/templates/azure-cli.rb.in" \ --output "azure-cli.rb" Environment variable fallbacks (used when a CLI flag is omitted): - VERSION, ARM64_SHA, X86_64_SHA, GITHUB_REPO, TEMPLATE, OUTPUT + VERSION, ARM64_MACOS_SHA, X86_64_MACOS_SHA, ARM64_LINUX_SHA, X86_64_LINUX_SHA, + GITHUB_REPO, TEMPLATE, OUTPUT """ import argparse import os from pathlib import Path -from typing import Dict DEFAULT_TEMPLATE = "scripts/release/macos/templates/azure-cli.rb.in" DEFAULT_OUTPUT = "azure-cli.rb" @@ -37,7 +39,7 @@ def _require(value: str, name: str) -> str: return value -def _render_template(template_path: Path, replacements: Dict[str, str]) -> str: +def _render_template(template_path: Path, replacements: dict[str, str]) -> str: content = template_path.read_text() for key, value in replacements.items(): content = content.replace(key, value) @@ -51,8 +53,10 @@ def _render_template(template_path: Path, replacements: Dict[str, str]) -> str: def generate_cask(args: argparse.Namespace) -> None: version = _require(_env_or_arg(args.version, "VERSION"), "version") - arm64_sha = _require(_env_or_arg(args.arm64_sha, "ARM64_SHA"), "arm64_sha") - x86_64_sha = _require(_env_or_arg(args.x86_64_sha, "X86_64_SHA"), "x86_64_sha") + arm64_macos_sha = _require(_env_or_arg(args.arm64_macos_sha, "ARM64_MACOS_SHA"), "arm64_macos_sha") + x86_64_macos_sha = _require(_env_or_arg(args.x86_64_macos_sha, "X86_64_MACOS_SHA"), "x86_64_macos_sha") + arm64_linux_sha = _require(_env_or_arg(args.arm64_linux_sha, "ARM64_LINUX_SHA"), "arm64_linux_sha") + x86_64_linux_sha = _require(_env_or_arg(args.x86_64_linux_sha, "X86_64_LINUX_SHA"), "x86_64_linux_sha") github_repo = _require(_env_or_arg(args.github_repo, "GITHUB_REPO"), "github_repo") python_version = _require(_env_or_arg(args.python_version, "PYTHON_VERSION"), "python_version") @@ -61,8 +65,10 @@ def generate_cask(args: argparse.Namespace) -> None: replacements = { "{{ version }}": version, - "{{ arm64_sha }}": arm64_sha, - "{{ x86_64_sha }}": x86_64_sha, + "{{ arm64_macos_sha }}": arm64_macos_sha, + "{{ x86_64_macos_sha }}": x86_64_macos_sha, + "{{ arm64_linux_sha }}": arm64_linux_sha, + "{{ x86_64_linux_sha }}": x86_64_linux_sha, "{{ github_repo }}": github_repo, "{{ python_version }}": python_version, } @@ -74,8 +80,10 @@ def generate_cask(args: argparse.Namespace) -> None: def main() -> None: parser = argparse.ArgumentParser(prog="cask_generate.py") parser.add_argument("--version", dest="version", help="Azure CLI version") - parser.add_argument("--arm64-sha", dest="arm64_sha", help="ARM64 tarball SHA256") - parser.add_argument("--x86-64-sha", dest="x86_64_sha", help="x86_64 tarball SHA256") + parser.add_argument("--arm64-macos-sha", dest="arm64_macos_sha", help="macOS ARM64 tarball SHA256") + parser.add_argument("--x86-64-macos-sha", dest="x86_64_macos_sha", help="macOS x86_64 tarball SHA256") + parser.add_argument("--arm64-linux-sha", dest="arm64_linux_sha", help="Linux ARM64 tarball SHA256") + parser.add_argument("--x86-64-linux-sha", dest="x86_64_linux_sha", help="Linux x86_64 tarball SHA256") parser.add_argument("--github-repo", dest="github_repo", help="GitHub repo, e.g. Azure/azure-cli") parser.add_argument("--python-version", dest="python_version", help="Python major.minor version, e.g. 3.14") parser.add_argument("--template", dest="template", help="Template path (.rb.in)") diff --git a/scripts/release/macos/templates/az_launcher.sh.in b/scripts/release/macos/templates/az_launcher.sh.in index f11971e0f6d..8e221e321ea 100644 --- a/scripts/release/macos/templates/az_launcher.sh.in +++ b/scripts/release/macos/templates/az_launcher.sh.in @@ -4,7 +4,7 @@ set -euo pipefail # Azure CLI Launcher (Homebrew Cask & Offline/Tarball) # # Install Mode: -# - Cask path detected: use Homebrew Python +# - Homebrew Cask path detected: use Homebrew Python # - Other paths: treat as tarball and require AZ_PYTHON PY_VER="{PYTHON_MAJOR_MINOR}" @@ -28,7 +28,9 @@ INSTALL_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" AZURE_CLI_SITE_PACKAGES="$INSTALL_DIR/libexec/lib/python${PY_VER}/site-packages" -if [[ "$SCRIPT_PATH" == /opt/homebrew/Caskroom/* ]] || [[ "$SCRIPT_PATH" == /usr/local/Caskroom/* ]]; then +if [[ "$SCRIPT_PATH" == /opt/homebrew/Caskroom/* ]] || \ + [[ "$SCRIPT_PATH" == /usr/local/Caskroom/* ]] || \ + [[ "$SCRIPT_PATH" == /home/linuxbrew/.linuxbrew/Caskroom/* ]]; then INSTALLER="HOMEBREW_CASK" else INSTALLER="TARBALL" @@ -43,6 +45,10 @@ if [[ "$INSTALLER" == "HOMEBREW_CASK" ]]; then PYTHON="/opt/homebrew/bin/python${PY_VER}" elif [[ -x "/usr/local/bin/python${PY_VER}" ]]; then PYTHON="/usr/local/bin/python${PY_VER}" + elif [[ -x "/home/linuxbrew/.linuxbrew/opt/python@${PY_VER}/libexec/bin/${PY_BIN}" ]]; then + PYTHON="/home/linuxbrew/.linuxbrew/opt/python@${PY_VER}/libexec/bin/${PY_BIN}" + elif [[ -x "/home/linuxbrew/.linuxbrew/bin/python${PY_VER}" ]]; then + PYTHON="/home/linuxbrew/.linuxbrew/bin/python${PY_VER}" else echo "Error: Python ${PY_VER} not found." >&2 echo "Install via Homebrew: brew install python@${PY_VER}" >&2 diff --git a/scripts/release/macos/templates/azure-cli.rb.in b/scripts/release/macos/templates/azure-cli.rb.in index 6941ba1fe62..ed6eb4ec2da 100644 --- a/scripts/release/macos/templates/azure-cli.rb.in +++ b/scripts/release/macos/templates/azure-cli.rb.in @@ -1,11 +1,14 @@ cask "azure-cli" do arch arm: "arm64", intel: "x86_64" + os macos: "macos", linux: "linux" version "{{ version }}" - sha256 arm: "{{ arm64_sha }}", - intel: "{{ x86_64_sha }}" + sha256 arm: "{{ arm64_macos_sha }}", + intel: "{{ x86_64_macos_sha }}", + arm64_linux: "{{ arm64_linux_sha }}", + x86_64_linux: "{{ x86_64_linux_sha }}" - url "https://github.com/{{ github_repo }}/releases/download/azure-cli-#{version}/azure-cli-#{version}-macos-#{arch}.tar.gz", + url "https://github.com/{{ github_repo }}/releases/download/azure-cli-#{version}/azure-cli-#{version}-#{os}-#{arch}.tar.gz", verified: "github.com/{{ github_repo }}/" name "Azure CLI" desc "Microsoft Azure CLI 2.0" From 26819311401c4a17bc9c5869fb189fd81bb15140 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Tue, 11 Aug 2026 09:23:46 +1000 Subject: [PATCH 02/24] Refactor standalone tarball release tooling --- .azure-pipelines/templates/linux/linux-build-jobs.yml | 2 +- .azure-pipelines/templates/macos/macos-build-jobs.yml | 2 +- .../macos/macos-cask-generation-and-tests.yml | 4 ++-- azure-pipelines.yml | 10 +++++++++- .../{macos => standalone}/build_binary_tar_gz.py | 0 scripts/release/{macos => standalone}/cask_generate.py | 6 +++--- .../resources/ThirdPartyNotices.txt | 0 .../{macos => standalone}/templates/az_launcher.sh.in | 0 .../{macos => standalone}/templates/azure-cli.rb.in | 0 9 files changed, 16 insertions(+), 8 deletions(-) rename scripts/release/{macos => standalone}/build_binary_tar_gz.py (100%) rename scripts/release/{macos => standalone}/cask_generate.py (95%) rename scripts/release/{macos => standalone}/resources/ThirdPartyNotices.txt (100%) rename scripts/release/{macos => standalone}/templates/az_launcher.sh.in (100%) rename scripts/release/{macos => standalone}/templates/azure-cli.rb.in (100%) diff --git a/.azure-pipelines/templates/linux/linux-build-jobs.yml b/.azure-pipelines/templates/linux/linux-build-jobs.yml index c46bc7fea62..4216eeed9a7 100644 --- a/.azure-pipelines/templates/linux/linux-build-jobs.yml +++ b/.azure-pipelines/templates/linux/linux-build-jobs.yml @@ -52,7 +52,7 @@ jobs: ARCH="$(Architecture)" OUTPUT_DIR="$(Build.ArtifactStagingDirectory)/cli-build" - "$(PythonPath)" scripts/release/macos/build_binary_tar_gz.py \ + "$(PythonPath)" scripts/release/standalone/build_binary_tar_gz.py \ --platform-tag "linux-$ARCH" \ --output-dir "$OUTPUT_DIR" diff --git a/.azure-pipelines/templates/macos/macos-build-jobs.yml b/.azure-pipelines/templates/macos/macos-build-jobs.yml index e67fb574b4f..aa727104a5d 100644 --- a/.azure-pipelines/templates/macos/macos-build-jobs.yml +++ b/.azure-pipelines/templates/macos/macos-build-jobs.yml @@ -80,7 +80,7 @@ jobs: echo "Architecture: $ARCH" $PYTHON --version - $PYTHON scripts/release/macos/build_binary_tar_gz.py \ + $PYTHON scripts/release/standalone/build_binary_tar_gz.py \ --platform-tag macos-$ARCH \ --output-dir dist/binary_tar_gz diff --git a/.azure-pipelines/templates/macos/macos-cask-generation-and-tests.yml b/.azure-pipelines/templates/macos/macos-cask-generation-and-tests.yml index e70924aabf8..e0fbc15ec21 100644 --- a/.azure-pipelines/templates/macos/macos-cask-generation-and-tests.yml +++ b/.azure-pipelines/templates/macos/macos-cask-generation-and-tests.yml @@ -148,7 +148,7 @@ jobs: # Generate cask using template with GitHub URLs (for publishing) mkdir -p $(Pipeline.Workspace)/macos-cask-definition - python3 "$REPO_ROOT/scripts/release/macos/cask_generate.py" \ + python3 "$REPO_ROOT/scripts/release/standalone/cask_generate.py" \ --version "$VERSION" \ --arm64-macos-sha "$ARM64_MACOS_SHA256" \ --x86-64-macos-sha "$X86_64_MACOS_SHA256" \ @@ -156,7 +156,7 @@ jobs: --x86-64-linux-sha "$X86_64_LINUX_SHA256" \ --github-repo "${{ parameters.GitHubRepo }}" \ --python-version "${{ parameters.PythonVersion }}" \ - --template "$REPO_ROOT/scripts/release/macos/templates/azure-cli.rb.in" \ + --template "$REPO_ROOT/scripts/release/standalone/templates/azure-cli.rb.in" \ --output "$(Pipeline.Workspace)/macos-cask-definition/azure-cli.rb" echo "=== Generated Cask (GitHub URLs - for publishing) ===" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8a268fa3f6a..08bc73dd4ab 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -763,6 +763,14 @@ jobs: condition: and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'Manual', 'Schedule')) dependsOn: ['ExtractMetadata'] +- template: .azure-pipelines/templates/linux/linux-build-jobs.yml + parameters: + PythonVersion: $(macos_cask_python_version) + LinuxArm64Pool: ${{ variables.ubuntu_arm64_pool }} + LinuxX64Pool: ${{ variables.ubuntu_pool }} + condition: and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'Manual', 'Schedule')) + dependsOn: ['ExtractMetadata'] + # Phase 2: Sign and notarize via ESRP - ${{ if eq(variables['System.TeamProject'], 'release') }}: - template: .azure-pipelines/templates/macos/macos-sign-notarize-jobs.yml @@ -784,7 +792,7 @@ jobs: PythonVersion: $(macos_cask_python_version) GitHubRepo: $(Build.Repository.Name) Debug: false - dependsOn: ['CreateFinalTarball'] + dependsOn: ['CreateFinalTarball', 'BuildLinuxCli'] - job: BuildRpmPackagesAzureLinux displayName: Build Rpm Package diff --git a/scripts/release/macos/build_binary_tar_gz.py b/scripts/release/standalone/build_binary_tar_gz.py similarity index 100% rename from scripts/release/macos/build_binary_tar_gz.py rename to scripts/release/standalone/build_binary_tar_gz.py diff --git a/scripts/release/macos/cask_generate.py b/scripts/release/standalone/cask_generate.py similarity index 95% rename from scripts/release/macos/cask_generate.py rename to scripts/release/standalone/cask_generate.py index ac6446beb44..c9088f5cf2c 100644 --- a/scripts/release/macos/cask_generate.py +++ b/scripts/release/standalone/cask_generate.py @@ -6,14 +6,14 @@ """Generate the Homebrew cask from a template. Usage: - python3 scripts/release/macos/cask_generate.py \ + python3 scripts/release/standalone/cask_generate.py \ --version "2.x.y" \ --arm64-macos-sha "" \ --x86-64-macos-sha "" \ --arm64-linux-sha "" \ --x86-64-linux-sha "" \ --github-repo "Azure/azure-cli" \ - --template "scripts/release/macos/templates/azure-cli.rb.in" \ + --template "scripts/release/standalone/templates/azure-cli.rb.in" \ --output "azure-cli.rb" Environment variable fallbacks (used when a CLI flag is omitted): @@ -25,7 +25,7 @@ import os from pathlib import Path -DEFAULT_TEMPLATE = "scripts/release/macos/templates/azure-cli.rb.in" +DEFAULT_TEMPLATE = "scripts/release/standalone/templates/azure-cli.rb.in" DEFAULT_OUTPUT = "azure-cli.rb" diff --git a/scripts/release/macos/resources/ThirdPartyNotices.txt b/scripts/release/standalone/resources/ThirdPartyNotices.txt similarity index 100% rename from scripts/release/macos/resources/ThirdPartyNotices.txt rename to scripts/release/standalone/resources/ThirdPartyNotices.txt diff --git a/scripts/release/macos/templates/az_launcher.sh.in b/scripts/release/standalone/templates/az_launcher.sh.in similarity index 100% rename from scripts/release/macos/templates/az_launcher.sh.in rename to scripts/release/standalone/templates/az_launcher.sh.in diff --git a/scripts/release/macos/templates/azure-cli.rb.in b/scripts/release/standalone/templates/azure-cli.rb.in similarity index 100% rename from scripts/release/macos/templates/azure-cli.rb.in rename to scripts/release/standalone/templates/azure-cli.rb.in From 9c8c318b95e1a542643da38f7df3ce11f1cbdae5 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Tue, 11 Aug 2026 17:41:16 +1000 Subject: [PATCH 03/24] Refactor standalone release pipeline for Linux Cask --- ...one-release.yml => standalone-release.yml} | 36 +++-- .../cask-generation-and-tests.yml} | 129 ++++++++++++++++-- .../publish-jobs.yml} | 61 +++++++-- azure-pipelines.yml | 16 ++- 4 files changed, 201 insertions(+), 41 deletions(-) rename .azure-pipelines/{macos-standalone-release.yml => standalone-release.yml} (81%) rename .azure-pipelines/templates/{macos/macos-cask-generation-and-tests.yml => standalone/cask-generation-and-tests.yml} (71%) rename .azure-pipelines/templates/{macos/macos-publish-jobs.yml => standalone/publish-jobs.yml} (86%) diff --git a/.azure-pipelines/macos-standalone-release.yml b/.azure-pipelines/standalone-release.yml similarity index 81% rename from .azure-pipelines/macos-standalone-release.yml rename to .azure-pipelines/standalone-release.yml index 1b663db575a..cf43a4cfae2 100644 --- a/.azure-pipelines/macos-standalone-release.yml +++ b/.azure-pipelines/standalone-release.yml @@ -1,17 +1,17 @@ -# Azure CLI - macOS Release Pipeline (Build → Sign → Test → Publish) +# Azure CLI - Standalone Release Pipeline (Build → Sign → Test → Publish) # -# Purpose: Complete end-to-end macOS release pipeline +# Purpose: Complete end-to-end standalone tarball release pipeline for macOS and Linux # Architecture: Chains 4 job templates in sequence # # Pipeline Flow: # 1. macos-build-jobs.yml → Build unsigned tarballs (ARM64 + Intel) # 2. macos-sign-notarize-jobs.yml → Sign and notarize via ESRP -# 3. macos-test-jobs.yml → Test cask (local file://) + offline install -# 4. macos-publish-jobs.yml → GitHub release + Homebrew cask update +# 3. cask-generation-and-tests.yml → Test cask (local file://) + offline install +# 4. publish-jobs.yml → GitHub release + Homebrew cask update # # Output Artifacts: -# - cli-build-unsigned-arm64, cli-build-unsigned-x86_64 (intermediate) -# - cli-signed-notarized-arm64, cli-signed-notarized-x86_64 (final) +# - macos-cli-signed-notarized-arm64, macos-cli-signed-notarized-x86_64 (final) +# - linux-cli-final-arm64, linux-cli-final-x86_64 (final) trigger: none @@ -100,10 +100,10 @@ variables: - name: skipNugetSecurityAnalysis value: true -name: macos-release-$(Build.BuildId) +name: standalone-release-$(Build.BuildId) # ============================================================================ -# JOBS: End-to-end macOS release flow +# JOBS: End-to-end standalone release flow # ============================================================================ jobs: # ============================================================================ @@ -153,10 +153,12 @@ jobs: # PHASE 3a: TEST (local file:// cask + offline install) # ============================================================================ - ${{ if eq(variables['System.TeamProject'], 'release') }}: - - template: templates/macos/macos-cask-generation-and-tests.yml + - template: templates/standalone/cask-generation-and-tests.yml parameters: MacosArm64Image: ${{ variables.macos_arm64_pool }} MacosIntelImage: ${{ variables.macos_intel_pool }} + LinuxArm64Pool: ${{ variables.ubuntu_arm64_pool }} + LinuxX64Pool: ${{ variables.ubuntu_pool }} PythonVersion: ${{ parameters.PythonVersion }} GitHubRepo: $(GitHubRepo) Debug: ${{ parameters.Debug }} @@ -165,14 +167,16 @@ jobs: - BuildLinuxCli # Jobs included: -# - TestTempTapCask (matrix: ARM64 + Intel) - tests cask with local file:// URLs -# - TestOfflineInstall (matrix: ARM64 + Intel) - tests direct tarball install +# - TestMacOSTempTapCask (matrix: ARM64 + Intel) - tests the macOS cask +# - TestLinuxTempTapCask (matrix: ARM64 + x86_64) - tests the Linux cask +# - TestMacOSOfflineInstall (matrix: ARM64 + Intel) - tests macOS tarballs +# - TestLinuxOfflineInstall (matrix: ARM64 + x86_64) - tests Linux tarballs # ============================================================================ # PHASE 3b: PUBLISH (GitHub + Homebrew tap) # ============================================================================ - ${{ if eq(variables['System.TeamProject'], 'release') }}: - - template: templates/macos/macos-publish-jobs.yml + - template: templates/standalone/publish-jobs.yml parameters: PublishToGitHub: ${{ parameters.PublishToGitHub }} UpdateHomebrew: ${{ parameters.UpdateHomebrew }} @@ -182,11 +186,15 @@ jobs: HomebrewTapRepo: ${{ parameters.HomebrewTapRepo }} MacosArm64Image: ${{ variables.macos_arm64_pool }} MacosIntelImage: ${{ variables.macos_intel_pool }} + LinuxArm64Pool: ${{ variables.ubuntu_arm64_pool }} + LinuxX64Pool: ${{ variables.ubuntu_pool }} PythonVersion: ${{ parameters.PythonVersion }} Debug: ${{ parameters.Debug }} dependsOn: - - TestTempTapCask - - TestOfflineInstall + - TestMacOSTempTapCask + - TestLinuxTempTapCask + - TestMacOSOfflineInstall + - TestLinuxOfflineInstall # Jobs included: # - CreateGitHubRelease (conditional) diff --git a/.azure-pipelines/templates/macos/macos-cask-generation-and-tests.yml b/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml similarity index 71% rename from .azure-pipelines/templates/macos/macos-cask-generation-and-tests.yml rename to .azure-pipelines/templates/standalone/cask-generation-and-tests.yml index e0fbc15ec21..a0ad9cf0e67 100644 --- a/.azure-pipelines/templates/macos/macos-cask-generation-and-tests.yml +++ b/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml @@ -1,4 +1,4 @@ -# macOS Cask Generation and Test Jobs Template +# Standalone Cask Generation and Test Jobs Template # # Purpose: Generate cask definition, test installation (using local file:// URLs), # and test offline tarball install @@ -7,6 +7,8 @@ # Parameters: # - MacosArm64Image: VM image for ARM64 tests # - MacosIntelImage: VM image for Intel tests +# - LinuxArm64Pool: pool for ARM64 Linux tests +# - LinuxX64Pool: pool for x86_64 Linux tests # - PythonVersion: Homebrew Python version for testing # - GitHubRepo: GitHub repository (for cask generation) # - Debug: Enable debug diagnostics @@ -17,7 +19,7 @@ # linux-cli-final-arm64, linux-cli-final-x86_64 # # Produces: -# - Artifact: macos-cask-definition (contains azure-cli.rb with GitHub URLs) +# - Artifact: cask-definition (contains azure-cli.rb with GitHub URLs) parameters: - name: MacosArm64Image @@ -26,6 +28,12 @@ parameters: - name: MacosIntelImage type: string default: 'macos-15' + - name: LinuxArm64Pool + type: string + default: 'pool-ubuntu-latest-arm64' + - name: LinuxX64Pool + type: string + default: 'pool-ubuntu-2204' - name: PythonVersion type: string default: '3.14' @@ -46,7 +54,7 @@ jobs: # ============================================================================ # JOB: GENERATE CASK AND TEST VIA TEMP HOMEBREW TAP (Both Architectures) # ============================================================================ -- job: TestTempTapCask +- job: TestMacOSTempTapCask displayName: 'macOS | Generate Cask and Test' dependsOn: ${{ parameters.dependsOn }} condition: ${{ parameters.condition }} @@ -147,7 +155,7 @@ jobs: fi # Generate cask using template with GitHub URLs (for publishing) - mkdir -p $(Pipeline.Workspace)/macos-cask-definition + mkdir -p $(Pipeline.Workspace)/cask-definition python3 "$REPO_ROOT/scripts/release/standalone/cask_generate.py" \ --version "$VERSION" \ --arm64-macos-sha "$ARM64_MACOS_SHA256" \ @@ -157,14 +165,14 @@ jobs: --github-repo "${{ parameters.GitHubRepo }}" \ --python-version "${{ parameters.PythonVersion }}" \ --template "$REPO_ROOT/scripts/release/standalone/templates/azure-cli.rb.in" \ - --output "$(Pipeline.Workspace)/macos-cask-definition/azure-cli.rb" + --output "$(Pipeline.Workspace)/cask-definition/azure-cli.rb" echo "=== Generated Cask (GitHub URLs - for publishing) ===" - cat $(Pipeline.Workspace)/macos-cask-definition/azure-cli.rb + cat $(Pipeline.Workspace)/cask-definition/azure-cli.rb # Copy to staging and modify for local file:// testing mkdir -p $(Pipeline.Workspace)/cask-staging - cp $(Pipeline.Workspace)/macos-cask-definition/azure-cli.rb $(Pipeline.Workspace)/cask-staging/azure-cli.rb + cp $(Pipeline.Workspace)/cask-definition/azure-cli.rb $(Pipeline.Workspace)/cask-staging/azure-cli.rb # Replace GitHub URL with file:// URL for local testing WORKSPACE_PATH="$(Pipeline.Workspace)" @@ -257,13 +265,76 @@ jobs: displayName: 'Publish cask definition artifact' condition: and(succeeded(), eq(variables['Architecture'], 'arm64')) inputs: - targetPath: '$(Pipeline.Workspace)/macos-cask-definition' - artifactName: 'macos-cask-definition' + targetPath: '$(Pipeline.Workspace)/cask-definition' + artifactName: 'cask-definition' + +# ============================================================================ +# JOB: TEST LINUX CASK VIA TEMP HOMEBREW TAP (Both Architectures) +# ============================================================================ +- job: TestLinuxTempTapCask + displayName: 'Linux | Generate Cask and Test' + dependsOn: TestMacOSTempTapCask + condition: ${{ parameters.condition }} + strategy: + matrix: + ARM64: + Architecture: 'arm64' + PoolName: ${{ parameters.LinuxArm64Pool }} + X64: + Architecture: 'x86_64' + PoolName: ${{ parameters.LinuxX64Pool }} + pool: + name: $(PoolName) + + steps: + - checkout: none + + - download: current + artifact: 'cask-definition' + displayName: 'Download cask definition' + + - download: current + artifact: 'linux-cli-final-$(Architecture)' + displayName: 'Download Linux tarball' + + - bash: | + set -euo pipefail + + brew uninstall azure-cli 2>/dev/null || true + brew uninstall --cask azure-cli 2>/dev/null || true + hash -r + + mkdir -p "$(Pipeline.Workspace)/cask-staging" + cp "$(Pipeline.Workspace)/cask-definition/azure-cli.rb" "$(Pipeline.Workspace)/cask-staging/azure-cli.rb" + sed -i.bak "s|url \"https://github.com/[^\"]*\"|url \"file://$(Pipeline.Workspace)/linux-cli-final-#{arch}/azure-cli-#{version}-linux-#{arch}.tar.gz\"|" \ + "$(Pipeline.Workspace)/cask-staging/azure-cli.rb" + sed -i.bak '/livecheck do/,/^ end$/d' "$(Pipeline.Workspace)/cask-staging/azure-cli.rb" + + git config --global user.email "test@pipeline.local" + git config --global user.name "Test Pipeline" + brew tap-new test/azure-cli-pipeline-test + TAP_PATH="$(brew --repository)/Library/Taps/test/homebrew-azure-cli-pipeline-test" + mkdir -p "$TAP_PATH/Casks" + cp "$(Pipeline.Workspace)/cask-staging/azure-cli.rb" "$TAP_PATH/Casks/" + cd "$TAP_PATH" + git add Casks/azure-cli.rb + git commit -m "Add azure-cli cask for testing" + + brew install python@${{ parameters.PythonVersion }} + brew install --cask test/azure-cli-pipeline-test/azure-cli + az version + displayName: 'Install and test Linux cask' + + - bash: | + brew uninstall --cask azure-cli 2>/dev/null || true + brew untap test/azure-cli-pipeline-test 2>/dev/null || true + displayName: 'Cleanup' + condition: always() # ============================================================================ # JOB: TEST OFFLINE/TARBALL INSTALLATION (Both Architectures) # ============================================================================ -- job: TestOfflineInstall +- job: TestMacOSOfflineInstall displayName: 'macOS | Test Offline Installation' dependsOn: ${{ parameters.dependsOn }} condition: ${{ parameters.condition }} @@ -331,3 +402,41 @@ jobs: rm -rf "$TEST_DIR" displayName: 'Test offline install' + +# ============================================================================ +# JOB: TEST LINUX OFFLINE/TARBALL INSTALLATION (Both Architectures) +# ============================================================================ +- job: TestLinuxOfflineInstall + displayName: 'Linux | Test Offline Installation' + dependsOn: ${{ parameters.dependsOn }} + condition: ${{ parameters.condition }} + strategy: + matrix: + ARM64: + Architecture: 'arm64' + PoolName: ${{ parameters.LinuxArm64Pool }} + X64: + Architecture: 'x86_64' + PoolName: ${{ parameters.LinuxX64Pool }} + pool: + name: $(PoolName) + + steps: + - checkout: none + + - download: current + artifact: 'linux-cli-final-$(Architecture)' + displayName: 'Download Linux tarball' + + - bash: | + set -euo pipefail + + TARBALL=$(find "$(Pipeline.Workspace)/linux-cli-final-$(Architecture)" -name "azure-cli-*-linux-$(Architecture).tar.gz" -print -quit) + TEST_DIR=$(mktemp -d) + trap 'rm -rf "$TEST_DIR"' EXIT + + tar -xzf "$TARBALL" -C "$TEST_DIR" + brew install python@${{ parameters.PythonVersion }} + export AZ_PYTHON="$(brew --prefix python@${{ parameters.PythonVersion }})/libexec/bin/python" + "$TEST_DIR/bin/az" version + displayName: 'Test Linux offline install' diff --git a/.azure-pipelines/templates/macos/macos-publish-jobs.yml b/.azure-pipelines/templates/standalone/publish-jobs.yml similarity index 86% rename from .azure-pipelines/templates/macos/macos-publish-jobs.yml rename to .azure-pipelines/templates/standalone/publish-jobs.yml index 4d313cbfecf..a506c9b012e 100644 --- a/.azure-pipelines/templates/macos/macos-publish-jobs.yml +++ b/.azure-pipelines/templates/standalone/publish-jobs.yml @@ -1,4 +1,4 @@ -# macOS Publish Jobs Template +# Standalone Publish Jobs Template # # Purpose: Publish signed tarballs to GitHub and update Homebrew cask # Usage: Called after test jobs pass @@ -11,13 +11,15 @@ # - HomebrewTapRepo: Homebrew tap repository # - MacosArm64Image: VM image for ARM64 tests # - MacosIntelImage: VM image for Intel tests +# - LinuxArm64Pool: pool for ARM64 Linux tests +# - LinuxX64Pool: pool for x86_64 Linux tests # - PythonVersion: Homebrew Python version for testing # - Debug: Enable debug diagnostics # - dependsOn: Jobs this depends on (typically test jobs) # # Requires: # - Artifacts: macos-cli-signed-notarized-arm64, macos-cli-signed-notarized-x86_64, -# linux-cli-final-arm64, linux-cli-final-x86_64, macos-cask-definition +# linux-cli-final-arm64, linux-cli-final-x86_64, cask-definition # - Repository resource: homebrewtap (for Homebrew cask updates) parameters: @@ -45,6 +47,12 @@ parameters: - name: MacosIntelImage type: string default: 'macos-15' + - name: LinuxArm64Pool + type: string + default: 'pool-ubuntu-latest-arm64' + - name: LinuxX64Pool + type: string + default: 'pool-ubuntu-2204' - name: PythonVersion type: string default: '3.14' @@ -56,7 +64,7 @@ parameters: default: 'succeeded()' - name: dependsOn type: object - default: ['TestTempTapCask', 'TestOfflineInstall'] + default: ['TestMacOSTempTapCask', 'TestLinuxTempTapCask', 'TestMacOSOfflineInstall', 'TestLinuxOfflineInstall'] jobs: # ============================================================================ @@ -64,7 +72,7 @@ jobs: # ============================================================================ - ${{ if eq(parameters.PublishToGitHub, true) }}: - job: CreateGitHubRelease - displayName: 'macOS | Create GitHub Release' + displayName: 'Standalone | Create GitHub Release' dependsOn: ${{ parameters.dependsOn }} condition: succeeded() pool: @@ -187,7 +195,7 @@ jobs: # ============================================================================ - ${{ if eq(parameters.UpdateHomebrew, true) }}: - job: UpdateHomebrewCask - displayName: 'macOS | Update Homebrew Cask' + displayName: 'Standalone | Update Homebrew Cask' dependsOn: - ${{ parameters.dependsOn }} - ${{ if eq(parameters.PublishToGitHub, true) }}: @@ -202,7 +210,7 @@ jobs: path: homebrew-tap - download: current - artifact: 'macos-cask-definition' + artifact: 'cask-definition' displayName: 'Download cask definition' - bash: | @@ -221,7 +229,7 @@ jobs: mkdir -p "$CASK_DIR" # Copy the pre-generated cask from artifact - cp $(Pipeline.Workspace)/macos-cask-definition/azure-cli.rb "$CASK_DIR/azure-cli.rb" + cp $(Pipeline.Workspace)/cask-definition/azure-cli.rb "$CASK_DIR/azure-cli.rb" echo "=== Cask Definition ===" cat "$CASK_DIR/azure-cli.rb" @@ -304,11 +312,41 @@ jobs: brew uninstall --cask azure-cli displayName: 'Test published cask install' + - job: TestPublishedLinuxCask + displayName: 'Linux | Test Published Cask' + dependsOn: UpdateHomebrewCask + condition: succeeded() + strategy: + matrix: + ARM64: + Architecture: 'arm64' + PoolName: ${{ parameters.LinuxArm64Pool }} + X64: + Architecture: 'x86_64' + PoolName: ${{ parameters.LinuxX64Pool }} + pool: + name: $(PoolName) + + steps: + - checkout: none + + - bash: | + set -euo pipefail + + brew uninstall azure-cli 2>/dev/null || true + brew uninstall --cask azure-cli 2>/dev/null || true + brew install python@${{ parameters.PythonVersion }} + brew tap ${{ parameters.HomebrewTapRepo }} + brew install --cask azure-cli + az version + brew uninstall --cask azure-cli + displayName: 'Test published Linux cask install' + # ============================================================================ # JOB: PRINT SUMMARY # ============================================================================ - job: PrintSummary - displayName: 'macOS | Print Summary' + displayName: 'Standalone | Print Summary' dependsOn: - ${{ parameters.dependsOn }} - ${{ if eq(parameters.PublishToGitHub, true) }}: @@ -317,6 +355,7 @@ jobs: - UpdateHomebrewCask - ${{ if and(eq(parameters.TestAfterPublish, true), eq(parameters.UpdateHomebrew, true)) }}: - TestPublishedCask + - TestPublishedLinuxCask condition: always() pool: vmImage: 'ubuntu-latest' @@ -326,7 +365,7 @@ jobs: - bash: | echo "╔══════════════════════════════════════════════════════════════════╗" - echo "║ AZURE CLI MACOS PUBLISH SUMMARY ║" + echo "║ AZURE CLI STANDALONE PUBLISH SUMMARY ║" echo "╠══════════════════════════════════════════════════════════════════╣" echo "║ Architectures: ARM64 (Apple Silicon) + x86_64 (Intel) ║" echo "║ GitHub Release: ${{ parameters.PublishToGitHub }} ║" @@ -337,6 +376,8 @@ jobs: echo "╠══════════════════════════════════════════════════════════════════╣" echo "║ • macos-cli-signed-notarized-arm64 (tarballs) ║" echo "║ • macos-cli-signed-notarized-x86_64 (tarballs) ║" - echo "║ • macos-cask-definition (azure-cli.rb) ║" + echo "║ • linux-cli-final-arm64 (tarballs) ║" + echo "║ • linux-cli-final-x86_64 (tarballs) ║" + echo "║ • cask-definition (azure-cli.rb) ║" echo "╚══════════════════════════════════════════════════════════════════╝" displayName: 'Print Summary' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 08bc73dd4ab..9bf30f1656f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -23,8 +23,8 @@ variables: - name: ComponentDetection.ForceScan value: eq(variables['Build.SourceBranch'], 'refs/heads/release') -# macOS Cask parameters -- name: macos_cask_python_version +# Standalone tarball and Cask parameters +- name: standalone_python_version value: '3.14' parameters: @@ -757,7 +757,7 @@ jobs: # Phase 1: Build unsigned tarballs (ARM64 + Intel) - template: .azure-pipelines/templates/macos/macos-build-jobs.yml parameters: - PythonVersion: $(macos_cask_python_version) + PythonVersion: $(standalone_python_version) MacosArm64Image: ${{ variables.macos_arm64_pool }} MacosIntelImage: ${{ variables.macos_intel_pool }} condition: and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'Manual', 'Schedule')) @@ -765,7 +765,7 @@ jobs: - template: .azure-pipelines/templates/linux/linux-build-jobs.yml parameters: - PythonVersion: $(macos_cask_python_version) + PythonVersion: $(standalone_python_version) LinuxArm64Pool: ${{ variables.ubuntu_arm64_pool }} LinuxX64Pool: ${{ variables.ubuntu_pool }} condition: and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'Manual', 'Schedule')) @@ -778,18 +778,20 @@ jobs: MacosArm64Image: ${{ variables.macos_arm64_pool }} MacosIntelImage: ${{ variables.macos_intel_pool }} BundleId: 'com.microsoft.azure.cli' - PythonVersion: $(macos_cask_python_version) + PythonVersion: $(standalone_python_version) ESRPServiceConnection: 'ame_esrp_connection' UseCurrentPipelineArtifacts: true dependsOn: ['VerifyMacOSCli'] # Phase 3: Generate cask and test via temp tap - ${{ if eq(variables['System.TeamProject'], 'release') }}: - - template: .azure-pipelines/templates/macos/macos-cask-generation-and-tests.yml + - template: .azure-pipelines/templates/standalone/cask-generation-and-tests.yml parameters: MacosArm64Image: ${{ variables.macos_arm64_pool }} MacosIntelImage: ${{ variables.macos_intel_pool }} - PythonVersion: $(macos_cask_python_version) + LinuxArm64Pool: ${{ variables.ubuntu_arm64_pool }} + LinuxX64Pool: ${{ variables.ubuntu_pool }} + PythonVersion: $(standalone_python_version) GitHubRepo: $(Build.Repository.Name) Debug: false dependsOn: ['CreateFinalTarball', 'BuildLinuxCli'] From 7e8ab3ae867380084481e013498fe87bed185b51 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Tue, 11 Aug 2026 17:44:06 +1000 Subject: [PATCH 04/24] Rename standalone validation pipeline --- ...lone-release.yml => mac-and-linux-standalone-release.yml} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename .azure-pipelines/{standalone-release.yml => mac-and-linux-standalone-release.yml} (96%) diff --git a/.azure-pipelines/standalone-release.yml b/.azure-pipelines/mac-and-linux-standalone-release.yml similarity index 96% rename from .azure-pipelines/standalone-release.yml rename to .azure-pipelines/mac-and-linux-standalone-release.yml index cf43a4cfae2..a431d5291a2 100644 --- a/.azure-pipelines/standalone-release.yml +++ b/.azure-pipelines/mac-and-linux-standalone-release.yml @@ -1,6 +1,7 @@ -# Azure CLI - Standalone Release Pipeline (Build → Sign → Test → Publish) +# Azure CLI - macOS and Linux Standalone Validation Pipeline # -# Purpose: Complete end-to-end standalone tarball release pipeline for macOS and Linux +# Purpose: Manually validate macOS and Linux standalone tarballs and the Homebrew Cask. +# This is an ad hoc testing pipeline, not the production release pipeline. # Architecture: Chains 4 job templates in sequence # # Pipeline Flow: From c140a9f3701682e406d9f2ade60f86c9378a6ecd Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Tue, 11 Aug 2026 18:04:19 +1000 Subject: [PATCH 05/24] Bootstrap Linuxbrew for standalone tests --- .../templates/linux/linux-build-jobs.yml | 2 ++ .../templates/linux/setup-homebrew.yml | 14 ++++++++++++++ .../standalone/cask-generation-and-tests.yml | 4 ++++ .../templates/standalone/publish-jobs.yml | 2 ++ 4 files changed, 22 insertions(+) create mode 100644 .azure-pipelines/templates/linux/setup-homebrew.yml diff --git a/.azure-pipelines/templates/linux/linux-build-jobs.yml b/.azure-pipelines/templates/linux/linux-build-jobs.yml index 4216eeed9a7..2b97059f557 100644 --- a/.azure-pipelines/templates/linux/linux-build-jobs.yml +++ b/.azure-pipelines/templates/linux/linux-build-jobs.yml @@ -38,6 +38,8 @@ jobs: - checkout: self fetchDepth: 1 + - template: setup-homebrew.yml + - bash: | set -euo pipefail diff --git a/.azure-pipelines/templates/linux/setup-homebrew.yml b/.azure-pipelines/templates/linux/setup-homebrew.yml new file mode 100644 index 00000000000..a07eca315d0 --- /dev/null +++ b/.azure-pipelines/templates/linux/setup-homebrew.yml @@ -0,0 +1,14 @@ +parameters: [] + +steps: +- bash: | + set -euo pipefail + + if ! command -v brew >/dev/null 2>&1; then + NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + fi + + eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + echo "##vso[task.prependpath]/home/linuxbrew/.linuxbrew/bin" + brew --version + displayName: 'Install Linuxbrew' \ No newline at end of file diff --git a/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml b/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml index a0ad9cf0e67..4282a58e632 100644 --- a/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml +++ b/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml @@ -289,6 +289,8 @@ jobs: steps: - checkout: none + - template: ../linux/setup-homebrew.yml + - download: current artifact: 'cask-definition' displayName: 'Download cask definition' @@ -424,6 +426,8 @@ jobs: steps: - checkout: none + - template: ../linux/setup-homebrew.yml + - download: current artifact: 'linux-cli-final-$(Architecture)' displayName: 'Download Linux tarball' diff --git a/.azure-pipelines/templates/standalone/publish-jobs.yml b/.azure-pipelines/templates/standalone/publish-jobs.yml index a506c9b012e..ea7852b996d 100644 --- a/.azure-pipelines/templates/standalone/publish-jobs.yml +++ b/.azure-pipelines/templates/standalone/publish-jobs.yml @@ -330,6 +330,8 @@ jobs: steps: - checkout: none + - template: ../linux/setup-homebrew.yml + - bash: | set -euo pipefail From d26b7778f66e1832f82b932babfa2462477d4222 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Tue, 11 Aug 2026 19:19:59 +1000 Subject: [PATCH 06/24] Trust temporary Homebrew Cask tap --- .../standalone/cask-generation-and-tests.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml b/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml index 4282a58e632..e59c0df80a5 100644 --- a/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml +++ b/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml @@ -198,6 +198,7 @@ jobs: # Create a temporary tap (named distinctly to avoid conflict with real Azure/homebrew-azure-cli) brew tap-new test/azure-cli-pipeline-test + brew trust test/azure-cli-pipeline-test # Get tap path TAP_PATH="$(brew --repository)/Library/Taps/test/homebrew-azure-cli-pipeline-test" @@ -225,6 +226,11 @@ jobs: set -ev echo "=== Installing Cask from Temporary Tap ($(Architecture)) ===" + + # Ensure neither the formula nor an existing cask can provide az. + brew uninstall azure-cli 2>/dev/null || true + brew uninstall --cask azure-cli 2>/dev/null || true + hash -r # Ensure Python is available if ! brew list python@${{ parameters.PythonVersion }} &>/dev/null; then @@ -237,6 +243,8 @@ jobs: # Verify installation echo "=== Verifying Installation ===" + brew info --cask azure-cli + brew info --cask azure-cli | grep -F "$TAP_PATH/Casks/azure-cli.rb" which az az --version @@ -315,6 +323,7 @@ jobs: git config --global user.email "test@pipeline.local" git config --global user.name "Test Pipeline" brew tap-new test/azure-cli-pipeline-test + brew trust test/azure-cli-pipeline-test TAP_PATH="$(brew --repository)/Library/Taps/test/homebrew-azure-cli-pipeline-test" mkdir -p "$TAP_PATH/Casks" cp "$(Pipeline.Workspace)/cask-staging/azure-cli.rb" "$TAP_PATH/Casks/" @@ -324,6 +333,8 @@ jobs: brew install python@${{ parameters.PythonVersion }} brew install --cask test/azure-cli-pipeline-test/azure-cli + brew info --cask azure-cli + brew info --cask azure-cli | grep -F "$TAP_PATH/Casks/azure-cli.rb" az version displayName: 'Install and test Linux cask' From 7c3ebc59ce3319c0ccdbd24671ace44426b792bd Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 06:56:30 +1000 Subject: [PATCH 07/24] Stabilize Linux tarball smoke test cleanup --- .azure-pipelines/templates/linux/linux-build-jobs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.azure-pipelines/templates/linux/linux-build-jobs.yml b/.azure-pipelines/templates/linux/linux-build-jobs.yml index 2b97059f557..864f94e2aa8 100644 --- a/.azure-pipelines/templates/linux/linux-build-jobs.yml +++ b/.azure-pipelines/templates/linux/linux-build-jobs.yml @@ -77,10 +77,11 @@ jobs: ARCH="$(Architecture)" TARBALL=$(find "$(Build.ArtifactStagingDirectory)/cli-build" -name "azure-cli-*-linux-$ARCH.tar.gz" -print -quit) EXTRACT_DIR=$(mktemp -d) - trap 'rm -rf "$EXTRACT_DIR"' EXIT + trap 'rm -rf "$EXTRACT_DIR" || true' EXIT tar -xzf "$TARBALL" -C "$EXTRACT_DIR" export AZ_PYTHON="$(PythonPath)" + export AZURE_CORE_COLLECT_TELEMETRY=False "$EXTRACT_DIR/bin/az" version displayName: 'Smoke test release tarball' From d504e9abb7db1b766d0f4cab6acef220d5dcf683 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 07:00:04 +1000 Subject: [PATCH 08/24] Disable PR trigger for standalone validation --- .azure-pipelines/mac-and-linux-standalone-release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.azure-pipelines/mac-and-linux-standalone-release.yml b/.azure-pipelines/mac-and-linux-standalone-release.yml index a431d5291a2..0e9ef1009af 100644 --- a/.azure-pipelines/mac-and-linux-standalone-release.yml +++ b/.azure-pipelines/mac-and-linux-standalone-release.yml @@ -15,6 +15,7 @@ # - linux-cli-final-arm64, linux-cli-final-x86_64 (final) trigger: none +pr: none parameters: # Build parameters From cc3d6b9e91f7f8113cc3a12b224b905e5c16f2c7 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 09:45:17 +1000 Subject: [PATCH 09/24] Use fork repositories for standalone release --- .azure-pipelines/mac-and-linux-standalone-release.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.azure-pipelines/mac-and-linux-standalone-release.yml b/.azure-pipelines/mac-and-linux-standalone-release.yml index 0e9ef1009af..53fa5ffdb92 100644 --- a/.azure-pipelines/mac-and-linux-standalone-release.yml +++ b/.azure-pipelines/mac-and-linux-standalone-release.yml @@ -39,7 +39,7 @@ parameters: - name: GitHubRepo displayName: 'GitHub Repository (owner/repo)' type: string - default: 'Azure/homebrew-azure-cli' + default: 'naga-nandyala/azure-cli' - name: UpdateHomebrew displayName: 'Update Homebrew cask after release' @@ -49,12 +49,12 @@ parameters: - name: HomebrewTapRepo displayName: 'Homebrew Tap Repository' type: string - default: 'Azure/homebrew-azure-cli' + default: 'naga-nandyala/homebrew-mycli-app' - name: GitHubServiceConnection displayName: 'GitHub Service Connection' type: string - default: 'Azure' + default: 'github.com_naga-nandyala' - name: ESRPServiceConnection displayName: 'ESRP Service Connection' @@ -70,8 +70,8 @@ resources: repositories: - repository: homebrewtap type: github - endpoint: 'Azure' - name: Azure/homebrew-azure-cli + endpoint: 'github.com_naga-nandyala' + name: naga-nandyala/homebrew-mycli-app ref: main variables: From 84bd263d9706c3e6d95d5d959d06b52f52767739 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 09:47:31 +1000 Subject: [PATCH 10/24] Use variable group for ESRP Apple key code --- .azure-pipelines/mac-and-linux-standalone-release.yml | 1 + .azure-pipelines/templates/macos/macos-sign-notarize-jobs.yml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.azure-pipelines/mac-and-linux-standalone-release.yml b/.azure-pipelines/mac-and-linux-standalone-release.yml index 53fa5ffdb92..f18caf253e2 100644 --- a/.azure-pipelines/mac-and-linux-standalone-release.yml +++ b/.azure-pipelines/mac-and-linux-standalone-release.yml @@ -78,6 +78,7 @@ variables: - template: templates/variables.yml - ${{ if eq(variables['System.TeamProject'], 'release') }}: - group: 'AME ESRP Variable Group' + - group: 'esrp-signing-codes' - name: GitHubRepo value: ${{ parameters.GitHubRepo }} diff --git a/.azure-pipelines/templates/macos/macos-sign-notarize-jobs.yml b/.azure-pipelines/templates/macos/macos-sign-notarize-jobs.yml index 3eda6928aa1..8b1199ac00d 100644 --- a/.azure-pipelines/templates/macos/macos-sign-notarize-jobs.yml +++ b/.azure-pipelines/templates/macos/macos-sign-notarize-jobs.yml @@ -235,7 +235,7 @@ jobs: inlineOperation: | [ { - "KeyCode": "CP-401337-Apple", + "KeyCode": "$(ESRPAppleKeyCode)", "OperationCode": "MacAppDeveloperSign", "ToolName": "sign", "ToolVersion": "1.0", @@ -454,7 +454,7 @@ jobs: inlineOperation: | [ { - "KeyCode": "CP-401337-Apple", + "KeyCode": "$(ESRPAppleKeyCode)", "OperationCode": "MacAppNotarize", "ToolName": "sign", "ToolVersion": "1.0", From c5091c4b6e2dbbf2e75f719db4f92659c914c065 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 10:47:06 +1000 Subject: [PATCH 11/24] Parallelize standalone platform validation --- .../mac-and-linux-standalone-release.yml | 10 ++++--- .../macos/macos-sign-notarize-jobs.yml | 2 +- .../standalone/cask-generation-and-tests.yml | 27 ++++++++++++------- azure-pipelines.yml | 4 ++- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/.azure-pipelines/mac-and-linux-standalone-release.yml b/.azure-pipelines/mac-and-linux-standalone-release.yml index f18caf253e2..150be3a02d6 100644 --- a/.azure-pipelines/mac-and-linux-standalone-release.yml +++ b/.azure-pipelines/mac-and-linux-standalone-release.yml @@ -149,7 +149,7 @@ jobs: # - SignBinaries (matrix: ARM64 + Intel) # - CreateNotarizeBundle (matrix: ARM64 + Intel) # - Notarize (matrix: ARM64 + Intel) -# - CreateFinalTarball (matrix: ARM64 + Intel) +# - CreateMacOSFinalTarball (matrix: ARM64 + Intel) # Artifacts: cli-signed-notarized-arm64, cli-signed-notarized-x86_64 # ============================================================================ @@ -165,8 +165,12 @@ jobs: PythonVersion: ${{ parameters.PythonVersion }} GitHubRepo: $(GitHubRepo) Debug: ${{ parameters.Debug }} - dependsOn: - - CreateFinalTarball + CaskDependsOn: + - CreateMacOSFinalTarball + - BuildLinuxCli + MacosDependsOn: + - CreateMacOSFinalTarball + LinuxDependsOn: - BuildLinuxCli # Jobs included: diff --git a/.azure-pipelines/templates/macos/macos-sign-notarize-jobs.yml b/.azure-pipelines/templates/macos/macos-sign-notarize-jobs.yml index 8b1199ac00d..5b009388419 100644 --- a/.azure-pipelines/templates/macos/macos-sign-notarize-jobs.yml +++ b/.azure-pipelines/templates/macos/macos-sign-notarize-jobs.yml @@ -475,7 +475,7 @@ jobs: # ============================================================================ # JOB: CREATE FINAL TARBALL (Both Architectures via Matrix) # ============================================================================ -- job: CreateFinalTarball +- job: CreateMacOSFinalTarball displayName: 'macOS | Create Final Tarball' dependsOn: - DownloadAnalyze diff --git a/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml b/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml index e59c0df80a5..1bfe5431a07 100644 --- a/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml +++ b/.azure-pipelines/templates/standalone/cask-generation-and-tests.yml @@ -12,7 +12,9 @@ # - PythonVersion: Homebrew Python version for testing # - GitHubRepo: GitHub repository (for cask generation) # - Debug: Enable debug diagnostics -# - dependsOn: Jobs this depends on (typically CreateFinalTarball) +# - CaskDependsOn: Jobs required to generate the cross-platform Cask +# - MacosDependsOn: Jobs required by macOS offline-install tests +# - LinuxDependsOn: Jobs required by Linux offline-install tests # # Requires: # - Artifacts: macos-cli-signed-notarized-arm64, macos-cli-signed-notarized-x86_64, @@ -46,9 +48,15 @@ parameters: - name: condition type: string default: 'succeeded()' - - name: dependsOn + - name: CaskDependsOn type: object - default: ['CreateFinalTarball'] + default: ['CreateMacOSFinalTarball', 'BuildLinuxCli'] + - name: MacosDependsOn + type: object + default: ['CreateMacOSFinalTarball'] + - name: LinuxDependsOn + type: object + default: ['BuildLinuxCli'] jobs: # ============================================================================ @@ -56,7 +64,7 @@ jobs: # ============================================================================ - job: TestMacOSTempTapCask displayName: 'macOS | Generate Cask and Test' - dependsOn: ${{ parameters.dependsOn }} + dependsOn: ${{ parameters.CaskDependsOn }} condition: ${{ parameters.condition }} strategy: matrix: @@ -244,7 +252,7 @@ jobs: # Verify installation echo "=== Verifying Installation ===" brew info --cask azure-cli - brew info --cask azure-cli | grep -F "$TAP_PATH/Casks/azure-cli.rb" + brew info --cask azure-cli | grep -F "github.com/test/homebrew-azure-cli-pipeline-test/blob/HEAD/Casks/azure-cli.rb" which az az --version @@ -334,7 +342,7 @@ jobs: brew install python@${{ parameters.PythonVersion }} brew install --cask test/azure-cli-pipeline-test/azure-cli brew info --cask azure-cli - brew info --cask azure-cli | grep -F "$TAP_PATH/Casks/azure-cli.rb" + brew info --cask azure-cli | grep -F "github.com/test/homebrew-azure-cli-pipeline-test/blob/HEAD/Casks/azure-cli.rb" az version displayName: 'Install and test Linux cask' @@ -349,7 +357,7 @@ jobs: # ============================================================================ - job: TestMacOSOfflineInstall displayName: 'macOS | Test Offline Installation' - dependsOn: ${{ parameters.dependsOn }} + dependsOn: ${{ parameters.MacosDependsOn }} condition: ${{ parameters.condition }} strategy: matrix: @@ -421,7 +429,7 @@ jobs: # ============================================================================ - job: TestLinuxOfflineInstall displayName: 'Linux | Test Offline Installation' - dependsOn: ${{ parameters.dependsOn }} + dependsOn: ${{ parameters.LinuxDependsOn }} condition: ${{ parameters.condition }} strategy: matrix: @@ -448,10 +456,11 @@ jobs: TARBALL=$(find "$(Pipeline.Workspace)/linux-cli-final-$(Architecture)" -name "azure-cli-*-linux-$(Architecture).tar.gz" -print -quit) TEST_DIR=$(mktemp -d) - trap 'rm -rf "$TEST_DIR"' EXIT + trap 'rm -rf "$TEST_DIR" || true' EXIT tar -xzf "$TARBALL" -C "$TEST_DIR" brew install python@${{ parameters.PythonVersion }} export AZ_PYTHON="$(brew --prefix python@${{ parameters.PythonVersion }})/libexec/bin/python" + export AZURE_CORE_COLLECT_TELEMETRY=False "$TEST_DIR/bin/az" version displayName: 'Test Linux offline install' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9bf30f1656f..190743ccf58 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -794,7 +794,9 @@ jobs: PythonVersion: $(standalone_python_version) GitHubRepo: $(Build.Repository.Name) Debug: false - dependsOn: ['CreateFinalTarball', 'BuildLinuxCli'] + CaskDependsOn: ['CreateMacOSFinalTarball', 'BuildLinuxCli'] + MacosDependsOn: ['CreateMacOSFinalTarball'] + LinuxDependsOn: ['BuildLinuxCli'] - job: BuildRpmPackagesAzureLinux displayName: Build Rpm Package From 55772908dc36fe4b85d8d01256e84c28ac78382b Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 12:01:31 +1000 Subject: [PATCH 12/24] Parameterize standalone release target branch --- .azure-pipelines/mac-and-linux-standalone-release.yml | 5 +++++ .azure-pipelines/templates/standalone/publish-jobs.yml | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.azure-pipelines/mac-and-linux-standalone-release.yml b/.azure-pipelines/mac-and-linux-standalone-release.yml index 150be3a02d6..c9079bfe974 100644 --- a/.azure-pipelines/mac-and-linux-standalone-release.yml +++ b/.azure-pipelines/mac-and-linux-standalone-release.yml @@ -40,6 +40,10 @@ parameters: displayName: 'GitHub Repository (owner/repo)' type: string default: 'naga-nandyala/azure-cli' + - name: GitHubTargetBranch + displayName: 'GitHub Release Target Branch' + type: string + default: 'dev' - name: UpdateHomebrew displayName: 'Update Homebrew cask after release' @@ -189,6 +193,7 @@ jobs: UpdateHomebrew: ${{ parameters.UpdateHomebrew }} TestAfterPublish: false GitHubRepo: $(GitHubRepo) + GitHubTargetBranch: ${{ parameters.GitHubTargetBranch }} GitHubServiceConnection: ${{ parameters.GitHubServiceConnection }} HomebrewTapRepo: ${{ parameters.HomebrewTapRepo }} MacosArm64Image: ${{ variables.macos_arm64_pool }} diff --git a/.azure-pipelines/templates/standalone/publish-jobs.yml b/.azure-pipelines/templates/standalone/publish-jobs.yml index ea7852b996d..b7b4277aa43 100644 --- a/.azure-pipelines/templates/standalone/publish-jobs.yml +++ b/.azure-pipelines/templates/standalone/publish-jobs.yml @@ -8,6 +8,7 @@ # - UpdateHomebrew: Whether to update Homebrew cask # - TestAfterPublish: Whether to test installation after publishing # - GitHubRepo: GitHub repository (owner/repo) +# - GitHubTargetBranch: Branch or commit to tag for the release # - HomebrewTapRepo: Homebrew tap repository # - MacosArm64Image: VM image for ARM64 tests # - MacosIntelImage: VM image for Intel tests @@ -35,6 +36,9 @@ parameters: - name: GitHubRepo type: string default: '' + - name: GitHubTargetBranch + type: string + default: 'main' - name: GitHubServiceConnection type: string default: 'Azure' @@ -165,7 +169,7 @@ jobs: gitHubConnection: '${{ parameters.GitHubServiceConnection }}' repositoryName: '${{ parameters.GitHubRepo }}' action: 'create' - target: 'main' + target: '${{ parameters.GitHubTargetBranch }}' tagSource: 'userSpecifiedTag' tag: '$(ReleaseTag)' title: 'Azure CLI $(AzureCliVersion) - Homebrew Cask' From 62d9b8f7e957973004208f098379fec976cd2c35 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 12:03:00 +1000 Subject: [PATCH 13/24] Add standalone publish recovery pipeline --- .azure-pipelines/test-standalone-publish.yml | 184 +++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 .azure-pipelines/test-standalone-publish.yml diff --git a/.azure-pipelines/test-standalone-publish.yml b/.azure-pipelines/test-standalone-publish.yml new file mode 100644 index 00000000000..c83a271f2f4 --- /dev/null +++ b/.azure-pipelines/test-standalone-publish.yml @@ -0,0 +1,184 @@ +# Temporary manual recovery pipeline for publishing artifacts from a completed +# macOS and Linux standalone validation run. +trigger: none +pr: none + +parameters: + - name: SourceBuildId + displayName: 'Source standalone build ID' + type: string + default: '' + - name: SourcePipelineDefinition + displayName: 'Source pipeline definition' + type: string + default: 'naga_mac-and-linux-standalone-release' + - name: GitHubRepo + displayName: 'GitHub release repository' + type: string + default: 'naga-nandyala/azure-cli' + - name: GitHubTargetBranch + displayName: 'GitHub release target branch' + type: string + default: 'dev' + - name: GitHubServiceConnection + displayName: 'GitHub service connection' + type: string + default: 'github.com_naga-nandyala' + +resources: + repositories: + - repository: homebrewtap + type: github + endpoint: 'github.com_naga-nandyala' + name: naga-nandyala/homebrew-mycli-app + ref: main + +name: standalone-publish-recovery-$(Build.BuildId) + +jobs: +- job: CreateGitHubRelease + displayName: 'Standalone | Create GitHub Release' + pool: + vmImage: 'ubuntu-latest' + + steps: + - checkout: none + + - task: DownloadPipelineArtifact@2 + displayName: 'Download macOS ARM64 release files' + inputs: + buildType: 'specific' + project: 'release' + definition: '${{ parameters.SourcePipelineDefinition }}' + buildVersionToDownload: 'specific' + buildId: '${{ parameters.SourceBuildId }}' + artifactName: 'macos-cli-signed-notarized-arm64' + targetPath: '$(Pipeline.Workspace)/macos-cli-signed-notarized-arm64' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download macOS x86_64 release files' + inputs: + buildType: 'specific' + project: 'release' + definition: '${{ parameters.SourcePipelineDefinition }}' + buildVersionToDownload: 'specific' + buildId: '${{ parameters.SourceBuildId }}' + artifactName: 'macos-cli-signed-notarized-x86_64' + targetPath: '$(Pipeline.Workspace)/macos-cli-signed-notarized-x86_64' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download Linux ARM64 release files' + inputs: + buildType: 'specific' + project: 'release' + definition: '${{ parameters.SourcePipelineDefinition }}' + buildVersionToDownload: 'specific' + buildId: '${{ parameters.SourceBuildId }}' + artifactName: 'linux-cli-final-arm64' + targetPath: '$(Pipeline.Workspace)/linux-cli-final-arm64' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download Linux x86_64 release files' + inputs: + buildType: 'specific' + project: 'release' + definition: '${{ parameters.SourcePipelineDefinition }}' + buildVersionToDownload: 'specific' + buildId: '${{ parameters.SourceBuildId }}' + artifactName: 'linux-cli-final-x86_64' + targetPath: '$(Pipeline.Workspace)/linux-cli-final-x86_64' + + - task: CopyFiles@2 + displayName: 'Collect release files' + inputs: + SourceFolder: '$(Pipeline.Workspace)' + Contents: | + macos-cli-signed-notarized-arm64/azure-cli-*.tar.gz + macos-cli-signed-notarized-arm64/azure-cli-*.tar.gz.sha256 + macos-cli-signed-notarized-x86_64/azure-cli-*.tar.gz + macos-cli-signed-notarized-x86_64/azure-cli-*.tar.gz.sha256 + linux-cli-final-arm64/azure-cli-*.tar.gz + linux-cli-final-arm64/azure-cli-*.tar.gz.sha256 + linux-cli-final-x86_64/azure-cli-*.tar.gz + linux-cli-final-x86_64/azure-cli-*.tar.gz.sha256 + TargetFolder: '$(Build.ArtifactStagingDirectory)/release' + flattenFolders: true + + - bash: | + set -euo pipefail + + ARM64_TARBALL=$(find "$(Build.ArtifactStagingDirectory)/release" -name 'azure-cli-*-macos-arm64.tar.gz' -print -quit) + VERSION=$(basename "$ARM64_TARBALL" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+([a-z0-9]+)?' | head -1) + + echo "Source build: ${{ parameters.SourceBuildId }}" + echo "Release version: $VERSION" + ls -lh "$(Build.ArtifactStagingDirectory)/release" + echo "##vso[task.setvariable variable=ReleaseTag]azure-cli-$VERSION" + echo "##vso[task.setvariable variable=AzureCliVersion]$VERSION" + displayName: 'Prepare release files' + + - task: GitHubRelease@1 + displayName: 'Delete existing release if present' + continueOnError: true + inputs: + gitHubConnection: '${{ parameters.GitHubServiceConnection }}' + repositoryName: '${{ parameters.GitHubRepo }}' + action: 'delete' + tag: '$(ReleaseTag)' + + - task: GitHubRelease@1 + displayName: 'Create GitHub Release' + inputs: + gitHubConnection: '${{ parameters.GitHubServiceConnection }}' + repositoryName: '${{ parameters.GitHubRepo }}' + action: 'create' + target: '${{ parameters.GitHubTargetBranch }}' + tagSource: 'userSpecifiedTag' + tag: '$(ReleaseTag)' + title: 'Azure CLI $(AzureCliVersion) - Homebrew Cask' + releaseNotesSource: 'inline' + releaseNotesInline: 'Recovery publication from standalone build ${{ parameters.SourceBuildId }}.' + assets: '$(Build.ArtifactStagingDirectory)/release/*' + addChangeLog: false + +- job: UpdateHomebrewCask + displayName: 'Standalone | Update Homebrew Cask' + dependsOn: CreateGitHubRelease + condition: succeeded() + pool: + vmImage: 'ubuntu-latest' + + steps: + - checkout: homebrewtap + persistCredentials: true + path: homebrew-tap + + - task: DownloadPipelineArtifact@2 + displayName: 'Download generated Cask definition' + inputs: + buildType: 'specific' + project: 'release' + definition: '${{ parameters.SourcePipelineDefinition }}' + buildVersionToDownload: 'specific' + buildId: '${{ parameters.SourceBuildId }}' + artifactName: 'cask-definition' + targetPath: '$(Pipeline.Workspace)/cask-definition' + + - bash: | + set -euo pipefail + + cd "$(Pipeline.Workspace)/homebrew-tap" + git fetch origin main + git reset --hard origin/main + + CASK_DIR='Casks/a' + mkdir -p "$CASK_DIR" + cp "$(Pipeline.Workspace)/cask-definition/azure-cli.rb" "$CASK_DIR/azure-cli.rb" + + VERSION=$(grep -oP 'version "\K[^"]+' "$CASK_DIR/azure-cli.rb" | head -1) + git config user.name 'Azure Pipeline' + git config user.email 'azure-pipeline@microsoft.com' + git add "$CASK_DIR/azure-cli.rb" + git commit -m "Update azure-cli to $VERSION" || echo 'No Cask changes' + git push origin HEAD:main + displayName: 'Push Cask to Homebrew tap' \ No newline at end of file From 224aeb83ef47828b965fbc4104e926787d533010 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 13:34:22 +1000 Subject: [PATCH 14/24] Format standalone Cask checksums --- scripts/release/standalone/templates/azure-cli.rb.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/release/standalone/templates/azure-cli.rb.in b/scripts/release/standalone/templates/azure-cli.rb.in index ed6eb4ec2da..0aa76d0e9f6 100644 --- a/scripts/release/standalone/templates/azure-cli.rb.in +++ b/scripts/release/standalone/templates/azure-cli.rb.in @@ -3,10 +3,10 @@ cask "azure-cli" do os macos: "macos", linux: "linux" version "{{ version }}" - sha256 arm: "{{ arm64_macos_sha }}", - intel: "{{ x86_64_macos_sha }}", - arm64_linux: "{{ arm64_linux_sha }}", - x86_64_linux: "{{ x86_64_linux_sha }}" + sha256 arm: "{{ arm64_macos_sha }}", + intel: "{{ x86_64_macos_sha }}", + arm64_linux: "{{ arm64_linux_sha }}", + x86_64_linux: "{{ x86_64_linux_sha }}" url "https://github.com/{{ github_repo }}/releases/download/azure-cli-#{version}/azure-cli-#{version}-#{os}-#{arch}.tar.gz", verified: "github.com/{{ github_repo }}/" From 2a73b3fbf600090ff08c054a8a0c4fd287d7994d Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 14:31:50 +1000 Subject: [PATCH 15/24] Restore Azure standalone release targets --- .azure-pipelines/mac-and-linux-standalone-release.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.azure-pipelines/mac-and-linux-standalone-release.yml b/.azure-pipelines/mac-and-linux-standalone-release.yml index c9079bfe974..e38e6f22670 100644 --- a/.azure-pipelines/mac-and-linux-standalone-release.yml +++ b/.azure-pipelines/mac-and-linux-standalone-release.yml @@ -39,7 +39,7 @@ parameters: - name: GitHubRepo displayName: 'GitHub Repository (owner/repo)' type: string - default: 'naga-nandyala/azure-cli' + default: 'Azure/homebrew-azure-cli' - name: GitHubTargetBranch displayName: 'GitHub Release Target Branch' type: string @@ -53,12 +53,12 @@ parameters: - name: HomebrewTapRepo displayName: 'Homebrew Tap Repository' type: string - default: 'naga-nandyala/homebrew-mycli-app' + default: 'Azure/homebrew-azure-cli' - name: GitHubServiceConnection displayName: 'GitHub Service Connection' type: string - default: 'github.com_naga-nandyala' + default: 'Azure' - name: ESRPServiceConnection displayName: 'ESRP Service Connection' @@ -74,8 +74,8 @@ resources: repositories: - repository: homebrewtap type: github - endpoint: 'github.com_naga-nandyala' - name: naga-nandyala/homebrew-mycli-app + endpoint: 'Azure' + name: Azure/homebrew-azure-cli ref: main variables: From abf6a9872d4d0a80d0f873d520b096cd2e9332fb Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 14:33:45 +1000 Subject: [PATCH 16/24] Target standalone releases at main --- .azure-pipelines/mac-and-linux-standalone-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azure-pipelines/mac-and-linux-standalone-release.yml b/.azure-pipelines/mac-and-linux-standalone-release.yml index e38e6f22670..d73baa009a9 100644 --- a/.azure-pipelines/mac-and-linux-standalone-release.yml +++ b/.azure-pipelines/mac-and-linux-standalone-release.yml @@ -43,7 +43,7 @@ parameters: - name: GitHubTargetBranch displayName: 'GitHub Release Target Branch' type: string - default: 'dev' + default: 'main' - name: UpdateHomebrew displayName: 'Update Homebrew cask after release' From 3dec1eab0de2a6b6361f5efa77e12e14710aec1f Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 16:00:04 +1000 Subject: [PATCH 17/24] Use Azure recovery pipeline for standalone publishing --- .../mac-and-linux-standalone-release.yml | 5 ----- .../templates/standalone/publish-jobs.yml | 6 +----- .azure-pipelines/test-standalone-publish.yml | 14 +++++--------- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/.azure-pipelines/mac-and-linux-standalone-release.yml b/.azure-pipelines/mac-and-linux-standalone-release.yml index d73baa009a9..4ec0d944781 100644 --- a/.azure-pipelines/mac-and-linux-standalone-release.yml +++ b/.azure-pipelines/mac-and-linux-standalone-release.yml @@ -40,10 +40,6 @@ parameters: displayName: 'GitHub Repository (owner/repo)' type: string default: 'Azure/homebrew-azure-cli' - - name: GitHubTargetBranch - displayName: 'GitHub Release Target Branch' - type: string - default: 'main' - name: UpdateHomebrew displayName: 'Update Homebrew cask after release' @@ -193,7 +189,6 @@ jobs: UpdateHomebrew: ${{ parameters.UpdateHomebrew }} TestAfterPublish: false GitHubRepo: $(GitHubRepo) - GitHubTargetBranch: ${{ parameters.GitHubTargetBranch }} GitHubServiceConnection: ${{ parameters.GitHubServiceConnection }} HomebrewTapRepo: ${{ parameters.HomebrewTapRepo }} MacosArm64Image: ${{ variables.macos_arm64_pool }} diff --git a/.azure-pipelines/templates/standalone/publish-jobs.yml b/.azure-pipelines/templates/standalone/publish-jobs.yml index b7b4277aa43..ea7852b996d 100644 --- a/.azure-pipelines/templates/standalone/publish-jobs.yml +++ b/.azure-pipelines/templates/standalone/publish-jobs.yml @@ -8,7 +8,6 @@ # - UpdateHomebrew: Whether to update Homebrew cask # - TestAfterPublish: Whether to test installation after publishing # - GitHubRepo: GitHub repository (owner/repo) -# - GitHubTargetBranch: Branch or commit to tag for the release # - HomebrewTapRepo: Homebrew tap repository # - MacosArm64Image: VM image for ARM64 tests # - MacosIntelImage: VM image for Intel tests @@ -36,9 +35,6 @@ parameters: - name: GitHubRepo type: string default: '' - - name: GitHubTargetBranch - type: string - default: 'main' - name: GitHubServiceConnection type: string default: 'Azure' @@ -169,7 +165,7 @@ jobs: gitHubConnection: '${{ parameters.GitHubServiceConnection }}' repositoryName: '${{ parameters.GitHubRepo }}' action: 'create' - target: '${{ parameters.GitHubTargetBranch }}' + target: 'main' tagSource: 'userSpecifiedTag' tag: '$(ReleaseTag)' title: 'Azure CLI $(AzureCliVersion) - Homebrew Cask' diff --git a/.azure-pipelines/test-standalone-publish.yml b/.azure-pipelines/test-standalone-publish.yml index c83a271f2f4..cf331bd95d8 100644 --- a/.azure-pipelines/test-standalone-publish.yml +++ b/.azure-pipelines/test-standalone-publish.yml @@ -15,22 +15,18 @@ parameters: - name: GitHubRepo displayName: 'GitHub release repository' type: string - default: 'naga-nandyala/azure-cli' - - name: GitHubTargetBranch - displayName: 'GitHub release target branch' - type: string - default: 'dev' + default: 'Azure/homebrew-azure-cli' - name: GitHubServiceConnection displayName: 'GitHub service connection' type: string - default: 'github.com_naga-nandyala' + default: 'Azure' resources: repositories: - repository: homebrewtap type: github - endpoint: 'github.com_naga-nandyala' - name: naga-nandyala/homebrew-mycli-app + endpoint: 'Azure' + name: Azure/homebrew-azure-cli ref: main name: standalone-publish-recovery-$(Build.BuildId) @@ -132,7 +128,7 @@ jobs: gitHubConnection: '${{ parameters.GitHubServiceConnection }}' repositoryName: '${{ parameters.GitHubRepo }}' action: 'create' - target: '${{ parameters.GitHubTargetBranch }}' + target: 'main' tagSource: 'userSpecifiedTag' tag: '$(ReleaseTag)' title: 'Azure CLI $(AzureCliVersion) - Homebrew Cask' From 1ee773cd955db2da525d034ab029e1466ffcfa94 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 16:37:30 +1000 Subject: [PATCH 18/24] Publish standalone recovery releases with PAT --- .../standalone/get-github-pat-token.yml | 46 +++++++++++++++++ .azure-pipelines/test-standalone-publish.yml | 50 +++++++++---------- 2 files changed, 70 insertions(+), 26 deletions(-) create mode 100644 .azure-pipelines/templates/standalone/get-github-pat-token.yml diff --git a/.azure-pipelines/templates/standalone/get-github-pat-token.yml b/.azure-pipelines/templates/standalone/get-github-pat-token.yml new file mode 100644 index 00000000000..7bee09d4da4 --- /dev/null +++ b/.azure-pipelines/templates/standalone/get-github-pat-token.yml @@ -0,0 +1,46 @@ +# Fetch a pre-minted GitHub PAT from Azure Key Vault for later steps. +# The Azure service connection needs Key Vault Secrets User (RBAC) or Get secret permission. +parameters: +- name: azureSubscription + type: string +- name: keyVaultName + type: string +- name: secretName + type: string +- name: outputVariable + type: string + default: GH_TOKEN +- name: execCondition + type: string + default: succeeded() + +steps: +- task: AzurePowerShell@5 + displayName: Get GitHub PAT from Key Vault + condition: ${{ parameters.execCondition }} + inputs: + azureSubscription: ${{ parameters.azureSubscription }} + azurePowerShellVersion: LatestVersion + pwsh: true + ScriptType: InlineScript + Inline: | + $keyVaultName = '${{ parameters.keyVaultName }}' + $secretName = '${{ parameters.secretName }}' + $outputVariable = '${{ parameters.outputVariable }}' + + if ($outputVariable -notmatch '^[A-Za-z_][A-Za-z0-9_.]*$') { + throw "outputVariable must be an Azure Pipelines variable name. Got: '$outputVariable'" + } + + try { + $ghToken = Get-AzKeyVaultSecret -VaultName $keyVaultName -Name $secretName -AsPlainText + } + catch { + throw "Failed to retrieve GitHub PAT secret '$secretName' from vault '$keyVaultName'. $($_.Exception.Message)" + } + + if ([string]::IsNullOrWhiteSpace($ghToken)) { + throw "GitHub PAT secret '$secretName' in vault '$keyVaultName' was empty or not found." + } + + Write-Host "##vso[task.setvariable variable=$outputVariable;issecret=true]$ghToken" diff --git a/.azure-pipelines/test-standalone-publish.yml b/.azure-pipelines/test-standalone-publish.yml index cf331bd95d8..1319448c520 100644 --- a/.azure-pipelines/test-standalone-publish.yml +++ b/.azure-pipelines/test-standalone-publish.yml @@ -16,10 +16,6 @@ parameters: displayName: 'GitHub release repository' type: string default: 'Azure/homebrew-azure-cli' - - name: GitHubServiceConnection - displayName: 'GitHub service connection' - type: string - default: 'Azure' resources: repositories: @@ -29,6 +25,10 @@ resources: name: Azure/homebrew-azure-cli ref: main +variables: +- ${{ if eq(variables['System.TeamProject'], 'release') }}: + - group: 'get-github-pat-token' + name: standalone-publish-recovery-$(Build.BuildId) jobs: @@ -40,6 +40,12 @@ jobs: steps: - checkout: none + - template: templates/standalone/get-github-pat-token.yml + parameters: + azureSubscription: $(AzureSubscription) + keyVaultName: $(GitHubPatKeyVault) + secretName: $(GitHubPatSecretName) + - task: DownloadPipelineArtifact@2 displayName: 'Download macOS ARM64 release files' inputs: @@ -113,29 +119,21 @@ jobs: echo "##vso[task.setvariable variable=AzureCliVersion]$VERSION" displayName: 'Prepare release files' - - task: GitHubRelease@1 - displayName: 'Delete existing release if present' - continueOnError: true - inputs: - gitHubConnection: '${{ parameters.GitHubServiceConnection }}' - repositoryName: '${{ parameters.GitHubRepo }}' - action: 'delete' - tag: '$(ReleaseTag)' + - bash: | + set -euo pipefail - - task: GitHubRelease@1 - displayName: 'Create GitHub Release' - inputs: - gitHubConnection: '${{ parameters.GitHubServiceConnection }}' - repositoryName: '${{ parameters.GitHubRepo }}' - action: 'create' - target: 'main' - tagSource: 'userSpecifiedTag' - tag: '$(ReleaseTag)' - title: 'Azure CLI $(AzureCliVersion) - Homebrew Cask' - releaseNotesSource: 'inline' - releaseNotesInline: 'Recovery publication from standalone build ${{ parameters.SourceBuildId }}.' - assets: '$(Build.ArtifactStagingDirectory)/release/*' - addChangeLog: false + release_tag='$(ReleaseTag)' + repository='${{ parameters.GitHubRepo }}' + + gh release delete "$release_tag" --repo "$repository" --yes || true + gh release create "$release_tag" "$(Build.ArtifactStagingDirectory)/release/"* \ + --repo "$repository" \ + --target main \ + --title "Azure CLI $(AzureCliVersion) - Homebrew Cask" \ + --notes "Recovery publication from standalone build ${{ parameters.SourceBuildId }}." + displayName: 'Create GitHub Release with PAT' + env: + GH_TOKEN: $(GH_TOKEN) - job: UpdateHomebrewCask displayName: 'Standalone | Update Homebrew Cask' From 981f45040709aceadba802defe901f3a1403903b Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 17:24:50 +1000 Subject: [PATCH 19/24] Temporarily test PAT publishing in azclips --- .azure-pipelines/test-standalone-publish.yml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/.azure-pipelines/test-standalone-publish.yml b/.azure-pipelines/test-standalone-publish.yml index 1319448c520..abe7bad2a52 100644 --- a/.azure-pipelines/test-standalone-publish.yml +++ b/.azure-pipelines/test-standalone-publish.yml @@ -15,7 +15,15 @@ parameters: - name: GitHubRepo displayName: 'GitHub release repository' type: string - default: 'Azure/homebrew-azure-cli' + default: 'Azure/azclips' + - name: ReleaseTagPrefix + displayName: 'GitHub release tag prefix' + type: string + default: 'standalone-publish-test' + - name: UpdateHomebrew + displayName: 'Update Homebrew Cask after release' + type: boolean + default: false resources: repositories: @@ -115,7 +123,7 @@ jobs: echo "Source build: ${{ parameters.SourceBuildId }}" echo "Release version: $VERSION" ls -lh "$(Build.ArtifactStagingDirectory)/release" - echo "##vso[task.setvariable variable=ReleaseTag]azure-cli-$VERSION" + echo "##vso[task.setvariable variable=ReleaseTag]${{ parameters.ReleaseTagPrefix }}-$(Build.BuildId)" echo "##vso[task.setvariable variable=AzureCliVersion]$VERSION" displayName: 'Prepare release files' @@ -125,12 +133,12 @@ jobs: release_tag='$(ReleaseTag)' repository='${{ parameters.GitHubRepo }}' - gh release delete "$release_tag" --repo "$repository" --yes || true gh release create "$release_tag" "$(Build.ArtifactStagingDirectory)/release/"* \ --repo "$repository" \ --target main \ - --title "Azure CLI $(AzureCliVersion) - Homebrew Cask" \ - --notes "Recovery publication from standalone build ${{ parameters.SourceBuildId }}." + --latest=false \ + --title "Standalone publish test $(Build.BuildId)" \ + --notes "Temporary standalone publishing test using artifacts from build ${{ parameters.SourceBuildId }}." displayName: 'Create GitHub Release with PAT' env: GH_TOKEN: $(GH_TOKEN) @@ -138,7 +146,7 @@ jobs: - job: UpdateHomebrewCask displayName: 'Standalone | Update Homebrew Cask' dependsOn: CreateGitHubRelease - condition: succeeded() + condition: and(succeeded(), eq('${{ parameters.UpdateHomebrew }}', true)) pool: vmImage: 'ubuntu-latest' From 362d229558d7091166943344ce10fcb3ed3230dc Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 12 Aug 2026 18:09:18 +1000 Subject: [PATCH 20/24] Revert "Temporarily test PAT publishing in azclips" This reverts commit 87318e4d9172bf33ffb58fd795bbdaf0046e8cca. --- .azure-pipelines/test-standalone-publish.yml | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/.azure-pipelines/test-standalone-publish.yml b/.azure-pipelines/test-standalone-publish.yml index abe7bad2a52..1319448c520 100644 --- a/.azure-pipelines/test-standalone-publish.yml +++ b/.azure-pipelines/test-standalone-publish.yml @@ -15,15 +15,7 @@ parameters: - name: GitHubRepo displayName: 'GitHub release repository' type: string - default: 'Azure/azclips' - - name: ReleaseTagPrefix - displayName: 'GitHub release tag prefix' - type: string - default: 'standalone-publish-test' - - name: UpdateHomebrew - displayName: 'Update Homebrew Cask after release' - type: boolean - default: false + default: 'Azure/homebrew-azure-cli' resources: repositories: @@ -123,7 +115,7 @@ jobs: echo "Source build: ${{ parameters.SourceBuildId }}" echo "Release version: $VERSION" ls -lh "$(Build.ArtifactStagingDirectory)/release" - echo "##vso[task.setvariable variable=ReleaseTag]${{ parameters.ReleaseTagPrefix }}-$(Build.BuildId)" + echo "##vso[task.setvariable variable=ReleaseTag]azure-cli-$VERSION" echo "##vso[task.setvariable variable=AzureCliVersion]$VERSION" displayName: 'Prepare release files' @@ -133,12 +125,12 @@ jobs: release_tag='$(ReleaseTag)' repository='${{ parameters.GitHubRepo }}' + gh release delete "$release_tag" --repo "$repository" --yes || true gh release create "$release_tag" "$(Build.ArtifactStagingDirectory)/release/"* \ --repo "$repository" \ --target main \ - --latest=false \ - --title "Standalone publish test $(Build.BuildId)" \ - --notes "Temporary standalone publishing test using artifacts from build ${{ parameters.SourceBuildId }}." + --title "Azure CLI $(AzureCliVersion) - Homebrew Cask" \ + --notes "Recovery publication from standalone build ${{ parameters.SourceBuildId }}." displayName: 'Create GitHub Release with PAT' env: GH_TOKEN: $(GH_TOKEN) @@ -146,7 +138,7 @@ jobs: - job: UpdateHomebrewCask displayName: 'Standalone | Update Homebrew Cask' dependsOn: CreateGitHubRelease - condition: and(succeeded(), eq('${{ parameters.UpdateHomebrew }}', true)) + condition: succeeded() pool: vmImage: 'ubuntu-latest' From fbac798dbc657c7e660f5d8835d19c31651ab825 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 19 Aug 2026 11:05:40 +1000 Subject: [PATCH 21/24] Remove standalone publish recovery pipeline --- .azure-pipelines/test-standalone-publish.yml | 178 ------------------- 1 file changed, 178 deletions(-) delete mode 100644 .azure-pipelines/test-standalone-publish.yml diff --git a/.azure-pipelines/test-standalone-publish.yml b/.azure-pipelines/test-standalone-publish.yml deleted file mode 100644 index 1319448c520..00000000000 --- a/.azure-pipelines/test-standalone-publish.yml +++ /dev/null @@ -1,178 +0,0 @@ -# Temporary manual recovery pipeline for publishing artifacts from a completed -# macOS and Linux standalone validation run. -trigger: none -pr: none - -parameters: - - name: SourceBuildId - displayName: 'Source standalone build ID' - type: string - default: '' - - name: SourcePipelineDefinition - displayName: 'Source pipeline definition' - type: string - default: 'naga_mac-and-linux-standalone-release' - - name: GitHubRepo - displayName: 'GitHub release repository' - type: string - default: 'Azure/homebrew-azure-cli' - -resources: - repositories: - - repository: homebrewtap - type: github - endpoint: 'Azure' - name: Azure/homebrew-azure-cli - ref: main - -variables: -- ${{ if eq(variables['System.TeamProject'], 'release') }}: - - group: 'get-github-pat-token' - -name: standalone-publish-recovery-$(Build.BuildId) - -jobs: -- job: CreateGitHubRelease - displayName: 'Standalone | Create GitHub Release' - pool: - vmImage: 'ubuntu-latest' - - steps: - - checkout: none - - - template: templates/standalone/get-github-pat-token.yml - parameters: - azureSubscription: $(AzureSubscription) - keyVaultName: $(GitHubPatKeyVault) - secretName: $(GitHubPatSecretName) - - - task: DownloadPipelineArtifact@2 - displayName: 'Download macOS ARM64 release files' - inputs: - buildType: 'specific' - project: 'release' - definition: '${{ parameters.SourcePipelineDefinition }}' - buildVersionToDownload: 'specific' - buildId: '${{ parameters.SourceBuildId }}' - artifactName: 'macos-cli-signed-notarized-arm64' - targetPath: '$(Pipeline.Workspace)/macos-cli-signed-notarized-arm64' - - - task: DownloadPipelineArtifact@2 - displayName: 'Download macOS x86_64 release files' - inputs: - buildType: 'specific' - project: 'release' - definition: '${{ parameters.SourcePipelineDefinition }}' - buildVersionToDownload: 'specific' - buildId: '${{ parameters.SourceBuildId }}' - artifactName: 'macos-cli-signed-notarized-x86_64' - targetPath: '$(Pipeline.Workspace)/macos-cli-signed-notarized-x86_64' - - - task: DownloadPipelineArtifact@2 - displayName: 'Download Linux ARM64 release files' - inputs: - buildType: 'specific' - project: 'release' - definition: '${{ parameters.SourcePipelineDefinition }}' - buildVersionToDownload: 'specific' - buildId: '${{ parameters.SourceBuildId }}' - artifactName: 'linux-cli-final-arm64' - targetPath: '$(Pipeline.Workspace)/linux-cli-final-arm64' - - - task: DownloadPipelineArtifact@2 - displayName: 'Download Linux x86_64 release files' - inputs: - buildType: 'specific' - project: 'release' - definition: '${{ parameters.SourcePipelineDefinition }}' - buildVersionToDownload: 'specific' - buildId: '${{ parameters.SourceBuildId }}' - artifactName: 'linux-cli-final-x86_64' - targetPath: '$(Pipeline.Workspace)/linux-cli-final-x86_64' - - - task: CopyFiles@2 - displayName: 'Collect release files' - inputs: - SourceFolder: '$(Pipeline.Workspace)' - Contents: | - macos-cli-signed-notarized-arm64/azure-cli-*.tar.gz - macos-cli-signed-notarized-arm64/azure-cli-*.tar.gz.sha256 - macos-cli-signed-notarized-x86_64/azure-cli-*.tar.gz - macos-cli-signed-notarized-x86_64/azure-cli-*.tar.gz.sha256 - linux-cli-final-arm64/azure-cli-*.tar.gz - linux-cli-final-arm64/azure-cli-*.tar.gz.sha256 - linux-cli-final-x86_64/azure-cli-*.tar.gz - linux-cli-final-x86_64/azure-cli-*.tar.gz.sha256 - TargetFolder: '$(Build.ArtifactStagingDirectory)/release' - flattenFolders: true - - - bash: | - set -euo pipefail - - ARM64_TARBALL=$(find "$(Build.ArtifactStagingDirectory)/release" -name 'azure-cli-*-macos-arm64.tar.gz' -print -quit) - VERSION=$(basename "$ARM64_TARBALL" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+([a-z0-9]+)?' | head -1) - - echo "Source build: ${{ parameters.SourceBuildId }}" - echo "Release version: $VERSION" - ls -lh "$(Build.ArtifactStagingDirectory)/release" - echo "##vso[task.setvariable variable=ReleaseTag]azure-cli-$VERSION" - echo "##vso[task.setvariable variable=AzureCliVersion]$VERSION" - displayName: 'Prepare release files' - - - bash: | - set -euo pipefail - - release_tag='$(ReleaseTag)' - repository='${{ parameters.GitHubRepo }}' - - gh release delete "$release_tag" --repo "$repository" --yes || true - gh release create "$release_tag" "$(Build.ArtifactStagingDirectory)/release/"* \ - --repo "$repository" \ - --target main \ - --title "Azure CLI $(AzureCliVersion) - Homebrew Cask" \ - --notes "Recovery publication from standalone build ${{ parameters.SourceBuildId }}." - displayName: 'Create GitHub Release with PAT' - env: - GH_TOKEN: $(GH_TOKEN) - -- job: UpdateHomebrewCask - displayName: 'Standalone | Update Homebrew Cask' - dependsOn: CreateGitHubRelease - condition: succeeded() - pool: - vmImage: 'ubuntu-latest' - - steps: - - checkout: homebrewtap - persistCredentials: true - path: homebrew-tap - - - task: DownloadPipelineArtifact@2 - displayName: 'Download generated Cask definition' - inputs: - buildType: 'specific' - project: 'release' - definition: '${{ parameters.SourcePipelineDefinition }}' - buildVersionToDownload: 'specific' - buildId: '${{ parameters.SourceBuildId }}' - artifactName: 'cask-definition' - targetPath: '$(Pipeline.Workspace)/cask-definition' - - - bash: | - set -euo pipefail - - cd "$(Pipeline.Workspace)/homebrew-tap" - git fetch origin main - git reset --hard origin/main - - CASK_DIR='Casks/a' - mkdir -p "$CASK_DIR" - cp "$(Pipeline.Workspace)/cask-definition/azure-cli.rb" "$CASK_DIR/azure-cli.rb" - - VERSION=$(grep -oP 'version "\K[^"]+' "$CASK_DIR/azure-cli.rb" | head -1) - git config user.name 'Azure Pipeline' - git config user.email 'azure-pipeline@microsoft.com' - git add "$CASK_DIR/azure-cli.rb" - git commit -m "Update azure-cli to $VERSION" || echo 'No Cask changes' - git push origin HEAD:main - displayName: 'Push Cask to Homebrew tap' \ No newline at end of file From 0e5a8ef957a0748b45d8e1cb743b63ee652a11ec Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 19 Aug 2026 11:58:35 +1000 Subject: [PATCH 22/24] Remove deprecated Cask verified parameter --- scripts/release/standalone/templates/azure-cli.rb.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/release/standalone/templates/azure-cli.rb.in b/scripts/release/standalone/templates/azure-cli.rb.in index 0aa76d0e9f6..15449ae347a 100644 --- a/scripts/release/standalone/templates/azure-cli.rb.in +++ b/scripts/release/standalone/templates/azure-cli.rb.in @@ -8,8 +8,7 @@ cask "azure-cli" do arm64_linux: "{{ arm64_linux_sha }}", x86_64_linux: "{{ x86_64_linux_sha }}" - url "https://github.com/{{ github_repo }}/releases/download/azure-cli-#{version}/azure-cli-#{version}-#{os}-#{arch}.tar.gz", - verified: "github.com/{{ github_repo }}/" + url "https://github.com/{{ github_repo }}/releases/download/azure-cli-#{version}/azure-cli-#{version}-#{os}-#{arch}.tar.gz" name "Azure CLI" desc "Microsoft Azure CLI 2.0" homepage "https://docs.microsoft.com/cli/azure/overview" From 4feaa88dd7f3ce1d3548c3450a56b0e893bc05a5 Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 19 Aug 2026 12:16:51 +1000 Subject: [PATCH 23/24] Use PAT for standalone GitHub releases --- .../mac-and-linux-standalone-release.yml | 7 +- .../templates/standalone/publish-jobs.yml | 83 +++++++++---------- 2 files changed, 42 insertions(+), 48 deletions(-) diff --git a/.azure-pipelines/mac-and-linux-standalone-release.yml b/.azure-pipelines/mac-and-linux-standalone-release.yml index 4ec0d944781..d5412dd3af3 100644 --- a/.azure-pipelines/mac-and-linux-standalone-release.yml +++ b/.azure-pipelines/mac-and-linux-standalone-release.yml @@ -51,11 +51,6 @@ parameters: type: string default: 'Azure/homebrew-azure-cli' - - name: GitHubServiceConnection - displayName: 'GitHub Service Connection' - type: string - default: 'Azure' - - name: ESRPServiceConnection displayName: 'ESRP Service Connection' type: string @@ -79,6 +74,7 @@ variables: - ${{ if eq(variables['System.TeamProject'], 'release') }}: - group: 'AME ESRP Variable Group' - group: 'esrp-signing-codes' + - group: 'get-github-pat-token' - name: GitHubRepo value: ${{ parameters.GitHubRepo }} @@ -189,7 +185,6 @@ jobs: UpdateHomebrew: ${{ parameters.UpdateHomebrew }} TestAfterPublish: false GitHubRepo: $(GitHubRepo) - GitHubServiceConnection: ${{ parameters.GitHubServiceConnection }} HomebrewTapRepo: ${{ parameters.HomebrewTapRepo }} MacosArm64Image: ${{ variables.macos_arm64_pool }} MacosIntelImage: ${{ variables.macos_intel_pool }} diff --git a/.azure-pipelines/templates/standalone/publish-jobs.yml b/.azure-pipelines/templates/standalone/publish-jobs.yml index ea7852b996d..4a8af7e7250 100644 --- a/.azure-pipelines/templates/standalone/publish-jobs.yml +++ b/.azure-pipelines/templates/standalone/publish-jobs.yml @@ -35,9 +35,6 @@ parameters: - name: GitHubRepo type: string default: '' - - name: GitHubServiceConnection - type: string - default: 'Azure' - name: HomebrewTapRepo type: string default: '' @@ -80,6 +77,12 @@ jobs: steps: - checkout: none + + - template: get-github-pat-token.yml + parameters: + azureSubscription: $(AzureSubscription) + keyVaultName: $(GitHubPatKeyVault) + secretName: $(GitHubPatSecretName) - download: current artifact: 'macos-cli-signed-notarized-arm64' @@ -150,45 +153,41 @@ jobs: displayName: 'Prepare release files' - - task: GitHubRelease@1 - displayName: 'Delete existing release if present' - continueOnError: true - inputs: - gitHubConnection: '${{ parameters.GitHubServiceConnection }}' - repositoryName: '${{ parameters.GitHubRepo }}' - action: 'delete' - tag: '$(ReleaseTag)' - - - task: GitHubRelease@1 - displayName: 'Create GitHub Release' - inputs: - gitHubConnection: '${{ parameters.GitHubServiceConnection }}' - repositoryName: '${{ parameters.GitHubRepo }}' - action: 'create' - target: 'main' - tagSource: 'userSpecifiedTag' - tag: '$(ReleaseTag)' - title: 'Azure CLI $(AzureCliVersion) - Homebrew Cask' - releaseNotesSource: 'inline' - releaseNotesInline: | - ## Azure CLI $(AzureCliVersion) - Homebrew Cask - - ### Available Architectures - - **Apple Silicon (arm64)**: azure-cli-$(AzureCliVersion)-macos-arm64.tar.gz - - **Intel (x86_64)**: azure-cli-$(AzureCliVersion)-macos-x86_64.tar.gz - - **Linux ARM64**: azure-cli-$(AzureCliVersion)-linux-arm64.tar.gz - - **Linux x86_64**: azure-cli-$(AzureCliVersion)-linux-x86_64.tar.gz - - ### Requirements - - Homebrew Python ${{ parameters.PythonVersion }} on macOS or Linux - - ### Installation via Homebrew Cask - ```bash - brew tap ${{ parameters.HomebrewTapRepo }} - brew install --cask azure-cli - ``` - assets: '$(Build.ArtifactStagingDirectory)/release/*' - addChangeLog: false + - bash: | + set -euo pipefail + + release_tag='$(ReleaseTag)' + repository='${{ parameters.GitHubRepo }}' + notes_file='$(Agent.TempDirectory)/release-notes.md' + + cat > "$notes_file" <<'EOF' + ## Azure CLI $(AzureCliVersion) - Homebrew Cask + + ### Available Architectures + - **Apple Silicon (arm64)**: azure-cli-$(AzureCliVersion)-macos-arm64.tar.gz + - **Intel (x86_64)**: azure-cli-$(AzureCliVersion)-macos-x86_64.tar.gz + - **Linux ARM64**: azure-cli-$(AzureCliVersion)-linux-arm64.tar.gz + - **Linux x86_64**: azure-cli-$(AzureCliVersion)-linux-x86_64.tar.gz + + ### Requirements + - Homebrew Python ${{ parameters.PythonVersion }} on macOS or Linux + + ### Installation via Homebrew Cask + ```bash + brew tap ${{ parameters.HomebrewTapRepo }} + brew install --cask azure-cli + ``` + EOF + + gh release delete "$release_tag" --repo "$repository" --yes || true + gh release create "$release_tag" "$(Build.ArtifactStagingDirectory)/release/"* \ + --repo "$repository" \ + --target main \ + --title "Azure CLI $(AzureCliVersion) - Homebrew Cask" \ + --notes-file "$notes_file" + displayName: 'Create GitHub Release with PAT' + env: + GH_TOKEN: $(GH_TOKEN) # ============================================================================ # JOB: UPDATE HOMEBREW CASK From ec16af80c553c2cf385eccfff7f45bf849fecede Mon Sep 17 00:00:00 2001 From: Naga Nandyala Date: Wed, 19 Aug 2026 12:25:26 +1000 Subject: [PATCH 24/24] Load ESRP signing codes in production pipeline --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 190743ccf58..75ce83fc57f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,6 +18,7 @@ variables: - template: ${{ variables.Pipeline.Workspace }}/.azure-pipelines/templates/variables.yml - ${{ if eq(variables['System.TeamProject'], 'release') }}: - group: 'AME ESRP Variable Group' + - group: 'esrp-signing-codes' - name: Codeql.Enabled value: false - name: ComponentDetection.ForceScan