Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
96a5552
macos - homebrew cask changes
naga-nandyala Mar 2, 2026
ea12486
updated comments
naga-nandyala Mar 2, 2026
15a0627
changed the connection details and test tap
naga-nandyala Mar 3, 2026
cf79ff1
removed hardcoded values
naga-nandyala Mar 3, 2026
274091a
Parameterize ESRPServiceConnection in sign-notarize template
naga-nandyala Mar 3, 2026
4c5469b
Rename temp test tap to azure-cli-pipeline-test to avoid conflict
naga-nandyala Mar 3, 2026
65c6dcb
Fix GitHub release target to use main branch of tap repo
naga-nandyala Mar 3, 2026
c27c060
Fix cask path to use sharded layout Casks/a/azure-cli.rb
naga-nandyala Mar 3, 2026
0a7f194
Add license header to build_binary_tar_gz.py
naga-nandyala Mar 4, 2026
bde477f
Address Copilot PR review: fix pool casing, README filename, docstring
naga-nandyala Mar 4, 2026
f314953
Address Copilot PR review round 2: template python version, env var f…
naga-nandyala Mar 4, 2026
6a6b75c
Pass PYTHON_MAJOR_MINOR env var to build script from pipeline parameter
naga-nandyala Mar 4, 2026
ee45a99
gh copilot review comments w.r.t pythonpath addressed
naga-nandyala Mar 9, 2026
dc4c6eb
tab completions change
naga-nandyala Mar 10, 2026
0977a3f
AZ_INSTALLER change & az upgrade change
naga-nandyala Mar 12, 2026
5463fe3
Legal File Changes
naga-nandyala Mar 12, 2026
5cf423a
Legal File Changes
naga-nandyala Mar 12, 2026
9095f1a
Fix cask push: reset to origin/main before updating to avoid rebase c…
naga-nandyala Mar 12, 2026
d35b220
Address Copilot review: fix misleading comment and remove unused para…
naga-nandyala Mar 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 184 additions & 0 deletions .azure-pipelines/macos-standalone-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Azure CLI - macOS Release Pipeline (Build → Sign → Test → Publish)
#
# Purpose: Complete end-to-end macOS release pipeline
# 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
#
# Output Artifacts:
# - cli-build-unsigned-arm64, cli-build-unsigned-x86_64 (intermediate)
# - cli-signed-notarized-arm64, cli-signed-notarized-x86_64 (final)

trigger: none

parameters:
# Build parameters
- name: PythonVersion
displayName: 'Python Version (Homebrew)'
type: string
default: '3.13'

# Sign/notarize parameters
- name: BundleId
displayName: 'Bundle ID for notarization'
type: string
default: 'com.microsoft.azure.cli'

# Publish parameters
- name: PublishToGitHub
displayName: 'Publish to GitHub Release'
type: boolean
default: true

- name: GitHubRepo
displayName: 'GitHub Repository (owner/repo)'
type: string
default: 'Azure/homebrew-azure-cli'

- name: UpdateHomebrew
displayName: 'Update Homebrew cask after release'
type: boolean
default: true

- name: HomebrewTapRepo
displayName: 'Homebrew Tap Repository'
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
default: 'ame_esrp_connection'

- name: Debug
displayName: 'Enable debug diagnostics'
type: boolean
default: false

resources:
repositories:
- repository: homebrewtap
type: github
endpoint: 'Azure'
name: Azure/homebrew-azure-cli
ref: main

variables:
- template: templates/variables.yml
- group: 'AME ESRP Variable Group'

- name: GitHubRepo
value: ${{ parameters.GitHubRepo }}
- name: HomebrewTapRepo
value: ${{ parameters.HomebrewTapRepo }}

# Disable auto-injection tasks
- name: Codeql.Enabled
value: false
- name: Codeql.SkipTaskAutoInjection
value: true
- name: CodeQL.enabled
value: false
- name: runCodesignValidationInjection
value: false
- name: DOTNET_CLI_TELEMETRY_OPTOUT
value: 1
- name: DOTNET_NOLOGO
value: 1
- name: NugetSecurityAnalysisWarningLevel
value: none
- name: skipNugetSecurityAnalysis
value: true

name: macos-release-$(Build.BuildId)

# ============================================================================
# JOBS: End-to-end macOS release flow
# ============================================================================
jobs:
# ============================================================================
# PHASE 1: BUILD (unsigned tarballs)
# ============================================================================
- template: templates/macos/macos-build-jobs.yml
parameters:
PythonVersion: ${{ parameters.PythonVersion }}
MacosArm64Image: ${{ variables.macos_arm64_pool }}
MacosIntelImage: ${{ variables.macos_intel_pool }}

# Jobs included:
# - BuildMacOSCli (matrix: ARM64 + Intel)
# - VerifyMacOSCli (matrix: ARM64 + Intel)
# Artifacts: cli-build-unsigned-arm64, cli-build-unsigned-x86_64

# ============================================================================
# PHASE 2: SIGN & NOTARIZE (via ESRP)
# ============================================================================
- template: templates/macos/macos-sign-notarize-jobs.yml
parameters:
BundleId: ${{ parameters.BundleId }}
PythonVersion: ${{ parameters.PythonVersion }}
MacosArm64Image: ${{ variables.macos_arm64_pool }}
MacosIntelImage: ${{ variables.macos_intel_pool }}
ESRPServiceConnection: ${{ parameters.ESRPServiceConnection }}
UseCurrentPipelineArtifacts: true
dependsOn:
- VerifyMacOSCli

# Jobs included:
# - DownloadAnalyze (matrix: ARM64 + Intel)
# - SignBinaries (matrix: ARM64 + Intel)
# - CreateNotarizeBundle (matrix: ARM64 + Intel)
# - Notarize (matrix: ARM64 + Intel)
# - CreateFinalTarball (matrix: ARM64 + Intel)
# Artifacts: cli-signed-notarized-arm64, cli-signed-notarized-x86_64

# ============================================================================
# PHASE 3a: TEST (local file:// cask + offline install)
# ============================================================================
- template: templates/macos/macos-cask-generation-and-tests.yml
parameters:
MacosArm64Image: ${{ variables.macos_arm64_pool }}
MacosIntelImage: ${{ variables.macos_intel_pool }}
PythonVersion: ${{ parameters.PythonVersion }}
GitHubRepo: $(GitHubRepo)
Debug: ${{ parameters.Debug }}
dependsOn:
- CreateFinalTarball

# Jobs included:
# - TestTempTapCask (matrix: ARM64 + Intel) - tests cask with local file:// URLs
# - TestOfflineInstall (matrix: ARM64 + Intel) - tests direct tarball install

# ============================================================================
# PHASE 3b: PUBLISH (GitHub + Homebrew tap)
# ============================================================================
- template: templates/macos/macos-publish-jobs.yml
parameters:
PublishToGitHub: ${{ parameters.PublishToGitHub }}
UpdateHomebrew: ${{ parameters.UpdateHomebrew }}
TestAfterPublish: false
GitHubRepo: $(GitHubRepo)
GitHubServiceConnection: ${{ parameters.GitHubServiceConnection }}
HomebrewTapRepo: ${{ parameters.HomebrewTapRepo }}
MacosArm64Image: ${{ variables.macos_arm64_pool }}
MacosIntelImage: ${{ variables.macos_intel_pool }}
PythonVersion: ${{ parameters.PythonVersion }}
Debug: ${{ parameters.Debug }}
dependsOn:
- TestTempTapCask
- TestOfflineInstall

# Jobs included:
# - CreateGitHubRelease (conditional)
# - UpdateHomebrewCask (conditional)
# - PrintSummary

186 changes: 186 additions & 0 deletions .azure-pipelines/templates/macos/macos-build-jobs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# macOS Build Jobs Template
#
# Purpose: Build Azure CLI tar.gz for macOS (ARM64 + Intel)
# Usage: Can be called from main pipeline or standalone wrapper
#
# Parameters:
# - PythonVersion: Homebrew Python version (default: 3.13)
# - MacosArm64Image: VM image for ARM64 builds
# - MacosIntelImage: VM image for Intel builds
# - condition: Job execution condition
# - dependsOn: Jobs this depends on
#
# Artifacts Published:
# - macos-cli-build-unsigned-arm64
# - macos-cli-build-unsigned-x86_64

parameters:
- name: PythonVersion
type: string
default: '3.13'
- name: MacosArm64Image
type: string
default: 'macos-15-arm64'
- name: MacosIntelImage
type: string
default: 'macos-15'
- name: condition
type: string
default: 'succeeded()'
- name: dependsOn
type: object
default: []

jobs:
# ============================================================================
# JOB: BUILD MACOS CLI (ARM64 + Intel via Matrix)
# ============================================================================
- job: BuildMacOSCli
displayName: 'macOS | Build CLI'
condition: ${{ parameters.condition }}
dependsOn: ${{ parameters.dependsOn }}
strategy:
matrix:
ARM64:
Architecture: 'arm64'
vmImageName: ${{ parameters.MacosArm64Image }}
Intel:
Architecture: 'x86_64'
vmImageName: ${{ parameters.MacosIntelImage }}
pool:
vmImage: $(vmImageName)
timeoutInMinutes: 60

steps:
- checkout: self
fetchDepth: 1

- bash: |
set -e

echo "=== Installing Homebrew Python ${{ parameters.PythonVersion }} ==="
echo "Architecture: $(Architecture)"
brew install python@${{ parameters.PythonVersion }}

PYTHON_PATH=$(brew --prefix python@${{ parameters.PythonVersion }})/libexec/bin/python3
echo "Python path: $PYTHON_PATH"
$PYTHON_PATH --version

echo "##vso[task.setvariable variable=PythonPath]$PYTHON_PATH"
displayName: 'Install Homebrew Python'

- bash: |
set -e

PYTHON="$(PythonPath)"
ARCH="$(Architecture)"

echo "=== Building Azure CLI with Homebrew Python ==="
echo "Python: $PYTHON"
echo "Architecture: $ARCH"
$PYTHON --version

$PYTHON scripts/release/macos/build_binary_tar_gz.py \
--platform-tag macos-$ARCH \
--output-dir dist/binary_tar_gz

echo ""
echo "=== Build Output ==="
ls -lh dist/binary_tar_gz/

mkdir -p $(Build.ArtifactStagingDirectory)/cli-build
cp dist/binary_tar_gz/*.tar.gz $(Build.ArtifactStagingDirectory)/cli-build/
cp dist/binary_tar_gz/*.sha256 $(Build.ArtifactStagingDirectory)/cli-build/

displayName: 'Build Azure CLI Tarball'
env:
PYTHON_MAJOR_MINOR: ${{ parameters.PythonVersion }}

- 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: 'macos-cli-build-unsigned-$(Architecture)'
displayName: 'Publish Unsigned CLI Build ($(Architecture))'

# ============================================================================
# JOB: VERIFY MACOS CLI (ARM64 + Intel via Matrix)
# ============================================================================
- job: VerifyMacOSCli
displayName: 'macOS | Verify CLI'
dependsOn: BuildMacOSCli
condition: succeeded()
strategy:
matrix:
ARM64:
Architecture: 'arm64'
vmImageName: ${{ parameters.MacosArm64Image }}
Intel:
Architecture: 'x86_64'
vmImageName: ${{ parameters.MacosIntelImage }}
pool:
vmImage: $(vmImageName)

steps:
- checkout: none

- download: current
artifact: 'macos-cli-build-unsigned-$(Architecture)'
displayName: 'Download CLI Build ($(Architecture))'

- bash: |
set -e

ARCH="$(Architecture)"
TARBALL=$(find $(Pipeline.Workspace)/macos-cli-build-unsigned-$ARCH -name "azure-cli-*-macos-$ARCH-nopython.tar.gz" | head -1)
EXTRACT_DIR=$(mktemp -d)

echo "=== Tarball Analysis ==="
echo "Architecture: $ARCH"
echo "Tarball: $(basename "$TARBALL")"
TARBALL_SIZE=$(du -h "$TARBALL" | cut -f1)
echo "Size: $TARBALL_SIZE"

tar -xzf "$TARBALL" -C "$EXTRACT_DIR"

echo ""
echo "=== Native Extensions (.so files) ==="
find "$EXTRACT_DIR" -name "*.so" -type f | wc -l

echo ""
echo "=== Tarball Contents Summary ==="
echo "Extracted size: $(du -sh "$EXTRACT_DIR" | cut -f1)"
echo "File count: $(find "$EXTRACT_DIR" -type f | wc -l)"

rm -rf "$EXTRACT_DIR"
displayName: 'Analyze Tarball ($(Architecture))'

- bash: |
set -e

ARCH="$(Architecture)"
TARBALL=$(find $(Pipeline.Workspace)/macos-cli-build-unsigned-$ARCH -name "azure-cli-*-macos-$ARCH-nopython.tar.gz" | head -1)
EXTRACT_DIR=$(mktemp -d)

echo "=== Verifying Azure CLI on $ARCH ==="
tar -xzf "$TARBALL" -C "$EXTRACT_DIR"

if ! brew list python@${{ parameters.PythonVersion }} &>/dev/null; then
brew install python@${{ parameters.PythonVersion }}
fi

AZ_PYTHON="$(brew --prefix python@${{ parameters.PythonVersion }})/libexec/bin/python3"
export AZ_PYTHON

echo "System architecture: $(uname -m)"

export AZ_DEBUG=1
"$EXTRACT_DIR/bin/az" version

echo ""
echo "Azure CLI works correctly on $ARCH"

rm -rf "$EXTRACT_DIR"
displayName: 'Verify CLI Works ($(Architecture))'
Loading
Loading