Skip to content

Commit 7cf929e

Browse files
jongioCopilot
andcommitted
feat: add microsoft.azd.exec extension
Add the azd-exec extension as microsoft.azd.exec to the official extensions directory. This extension enables script execution with Azure Developer CLI context, environment variables, and automatic Azure Key Vault secret resolution. Features: - Execute script files (-s) or inline commands (-i) with shell selection - Automatic azd environment variable loading via azd env get-values - Azure Key Vault secret reference resolution in environment variables - Cross-platform shell support (bash, sh, zsh, pwsh, powershell, cmd) - Child process exit code propagation for CI/CD pipelines - Interactive mode with stdin passthrough Architecture: - internal/cmd: Cobra CLI commands (root, version, listen) - internal/executor: Script execution engine with command builder - internal/envutil: Shared Key Vault reference resolution - internal/shellutil: Shared shell detection and validation Test coverage: 82-94% across all packages (42 tests). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 005d7ff commit 7cf929e

23 files changed

Lines changed: 2128 additions & 0 deletions
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: ext-microsoft-azd-exec-ci
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- "cli/azd/extensions/microsoft.azd.exec/**"
7+
- ".github/workflows/lint-ext-microsoft-azd-exec.yml"
8+
branches: [main]
9+
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
12+
cancel-in-progress: true
13+
14+
permissions:
15+
contents: read
16+
pull-requests: write
17+
18+
jobs:
19+
lint:
20+
uses: ./.github/workflows/lint-go.yml
21+
with:
22+
working-directory: cli/azd/extensions/microsoft.azd.exec
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
version: "2"
2+
3+
linters:
4+
default: none
5+
enable:
6+
- gosec
7+
- lll
8+
- unused
9+
- errorlint
10+
settings:
11+
lll:
12+
line-length: 220
13+
tab-width: 4
14+
15+
formatters:
16+
enable:
17+
- gofmt
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Changelog
2+
3+
## 0.5.0
4+
5+
- Initial release as `microsoft.azd.exec` in the Azure/azure-dev repository.
6+
- Execute scripts and inline commands with full azd environment context.
7+
- Cross-platform shell detection and execution (bash, sh, zsh, pwsh, powershell, cmd).
8+
- Interactive mode for scripts requiring stdin.
9+
- Child process exit code propagation for CI/CD pipelines.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# microsoft.azd.exec
2+
3+
Execute scripts and commands with Azure Developer CLI context and environment variables.
4+
5+
## Installation
6+
7+
```bash
8+
azd extension install microsoft.azd.exec
9+
```
10+
11+
## Usage
12+
13+
```bash
14+
# Execute a script file with azd environment
15+
azd exec ./setup.sh
16+
17+
# Inline command with environment variables
18+
azd exec 'echo $AZURE_ENV_NAME'
19+
20+
# Specify shell explicitly
21+
azd exec --shell pwsh "Write-Host 'Hello'"
22+
23+
# Pass arguments to script
24+
azd exec ./build.sh -- --verbose
25+
26+
# Interactive mode
27+
azd exec -i ./interactive-setup.sh
28+
```
29+
30+
## Features
31+
32+
- **Shell auto-detection**: Detects shell from file extension
33+
- **Cross-platform**: Supports bash, sh, zsh, pwsh, powershell, and cmd
34+
- **Interactive mode**: Connect stdin for scripts requiring user input
35+
- **Environment loading**: Inherits azd environment variables (including resolved Key Vault secrets from azd core)
36+
- **Exit code propagation**: Child process exit codes forwarded for CI/CD pipelines
37+
38+
## Development
39+
40+
```bash
41+
cd cli/azd/extensions/microsoft.azd.exec
42+
43+
# Build
44+
go build ./...
45+
46+
# Test
47+
go test ./...
48+
49+
# Build for all platforms
50+
EXTENSION_ID=microsoft.azd.exec EXTENSION_VERSION=0.5.0 ./build.sh
51+
```
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Ensure script fails on any error
2+
$ErrorActionPreference = 'Stop'
3+
4+
# Get the directory of the script
5+
$EXTENSION_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
6+
7+
# Change to the script directory
8+
Set-Location -Path $EXTENSION_DIR
9+
10+
# Create a safe version of EXTENSION_ID replacing dots with dashes
11+
$EXTENSION_ID_SAFE = $env:EXTENSION_ID -replace '\.', '-'
12+
13+
# Define output directory
14+
$OUTPUT_DIR = if ($env:OUTPUT_DIR) { $env:OUTPUT_DIR } else { Join-Path $EXTENSION_DIR "bin" }
15+
16+
# Create output directory if it doesn't exist
17+
if (-not (Test-Path -Path $OUTPUT_DIR)) {
18+
New-Item -ItemType Directory -Path $OUTPUT_DIR | Out-Null
19+
}
20+
21+
# List of OS and architecture combinations
22+
if ($env:EXTENSION_PLATFORM) {
23+
$PLATFORMS = @($env:EXTENSION_PLATFORM)
24+
}
25+
else {
26+
$PLATFORMS = @(
27+
"windows/amd64",
28+
"windows/arm64",
29+
"darwin/amd64",
30+
"darwin/arm64",
31+
"linux/amd64",
32+
"linux/arm64"
33+
)
34+
}
35+
36+
$APP_PATH = "$env:EXTENSION_ID/internal/cmd"
37+
38+
# Loop through platforms and build
39+
foreach ($PLATFORM in $PLATFORMS) {
40+
$OS, $ARCH = $PLATFORM -split '/'
41+
42+
$OUTPUT_NAME = Join-Path $OUTPUT_DIR "$EXTENSION_ID_SAFE-$OS-$ARCH"
43+
44+
if ($OS -eq "windows") {
45+
$OUTPUT_NAME += ".exe"
46+
}
47+
48+
Write-Host "Building for $OS/$ARCH..."
49+
50+
# Delete the output file if it already exists
51+
if (Test-Path -Path $OUTPUT_NAME) {
52+
Remove-Item -Path $OUTPUT_NAME -Force
53+
}
54+
55+
# Set environment variables for Go build
56+
$env:GOOS = $OS
57+
$env:GOARCH = $ARCH
58+
59+
go build `
60+
-trimpath `
61+
-buildmode=pie `
62+
-tags=cfi,cfg,osusergo `
63+
-ldflags="-s -w -X '$APP_PATH.Version=$env:EXTENSION_VERSION'" `
64+
-o $OUTPUT_NAME
65+
66+
if ($LASTEXITCODE -ne 0) {
67+
Write-Host "An error occurred while building for $OS/$ARCH"
68+
exit 1
69+
}
70+
}
71+
72+
Write-Host "Build completed successfully!"
73+
Write-Host "Binaries are located in the $OUTPUT_DIR directory."
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/bin/bash
2+
3+
# Get the directory of the script
4+
EXTENSION_DIR="$(cd "$(dirname "$0")" && pwd)"
5+
6+
# Change to the script directory
7+
cd "$EXTENSION_DIR" || exit
8+
9+
# Create a safe version of EXTENSION_ID replacing dots with dashes
10+
EXTENSION_ID_SAFE="${EXTENSION_ID//./-}"
11+
12+
# Define output directory
13+
OUTPUT_DIR="${OUTPUT_DIR:-$EXTENSION_DIR/bin}"
14+
15+
# Create output and target directories if they don't exist
16+
mkdir -p "$OUTPUT_DIR"
17+
18+
# Get Git commit hash and build date
19+
COMMIT=$(git rev-parse HEAD)
20+
BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
21+
22+
# List of OS and architecture combinations
23+
if [ -n "$EXTENSION_PLATFORM" ]; then
24+
PLATFORMS=("$EXTENSION_PLATFORM")
25+
else
26+
PLATFORMS=(
27+
"windows/amd64"
28+
"windows/arm64"
29+
"darwin/amd64"
30+
"darwin/arm64"
31+
"linux/amd64"
32+
"linux/arm64"
33+
)
34+
fi
35+
36+
APP_PATH="$EXTENSION_ID/internal/cmd"
37+
38+
# Loop through platforms and build
39+
for PLATFORM in "${PLATFORMS[@]}"; do
40+
OS=$(echo "$PLATFORM" | cut -d'/' -f1)
41+
ARCH=$(echo "$PLATFORM" | cut -d'/' -f2)
42+
43+
OUTPUT_NAME="$OUTPUT_DIR/$EXTENSION_ID_SAFE-$OS-$ARCH"
44+
45+
if [ "$OS" = "windows" ]; then
46+
OUTPUT_NAME+='.exe'
47+
fi
48+
49+
echo "Building for $OS/$ARCH..."
50+
51+
# Delete the output file if it already exists
52+
[ -f "$OUTPUT_NAME" ] && rm -f "$OUTPUT_NAME"
53+
54+
# Set environment variables for Go build
55+
GOOS=$OS GOARCH=$ARCH go build \
56+
-ldflags="-X '$APP_PATH.Version=$EXTENSION_VERSION' -X '$APP_PATH.Commit=$COMMIT' -X '$APP_PATH.BuildDate=$BUILD_DATE'" \
57+
-o "$OUTPUT_NAME"
58+
59+
if [ $? -ne 0 ]; then
60+
echo "An error occurred while building for $OS/$ARCH"
61+
exit 1
62+
fi
63+
done
64+
65+
echo "Build completed successfully!"
66+
echo "Binaries are located in the $OUTPUT_DIR directory."
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
param(
2+
[string] $Version = (Get-Content "$PSScriptRoot/../version.txt"),
3+
[string] $SourceVersion = (git rev-parse HEAD),
4+
[switch] $CodeCoverageEnabled,
5+
[string] $OutputFileName
6+
)
7+
$PSNativeCommandArgumentPassing = 'Legacy'
8+
9+
# Remove any previously built binaries
10+
go clean
11+
12+
if ($LASTEXITCODE) {
13+
Write-Host "Error running go clean"
14+
exit $LASTEXITCODE
15+
}
16+
17+
# Run `go help build` to obtain detailed information about `go build` flags.
18+
$buildFlags = @(
19+
# remove all file system paths from the resulting executable.
20+
"-trimpath",
21+
22+
# Use buildmode=pie (Position Independent Executable) for enhanced security.
23+
"-buildmode=pie"
24+
)
25+
26+
if ($CodeCoverageEnabled) {
27+
$buildFlags += "-cover"
28+
}
29+
30+
# Build constraint tags
31+
$tagsFlag = "-tags=cfi,cfg,osusergo"
32+
33+
# ld linker flags
34+
$ldFlag = "-ldflags=-s -w -X 'microsoft.azd.exec/internal/cmd.Version=$Version'"
35+
36+
if ($IsWindows) {
37+
Write-Host "Building for Windows"
38+
}
39+
elseif ($IsLinux) {
40+
Write-Host "Building for linux"
41+
}
42+
elseif ($IsMacOS) {
43+
Write-Host "Building for macOS"
44+
}
45+
46+
# Add output file flag based on specified output file name
47+
$outputFlag = "-o=$OutputFileName"
48+
49+
# collect flags
50+
$buildFlags += @(
51+
$tagsFlag,
52+
$ldFlag,
53+
$outputFlag
54+
)
55+
56+
function PrintFlags() {
57+
param(
58+
[string] $flags
59+
)
60+
61+
$i = 0
62+
foreach ($buildFlag in $buildFlags) {
63+
$argWithValue = $buildFlag.Split('=', 2)
64+
if ($argWithValue.Length -eq 2 -and !$argWithValue[1].StartsWith("`"")) {
65+
$buildFlag = "$($argWithValue[0])=`"$($argWithValue[1])`""
66+
}
67+
68+
if ($i -eq $buildFlags.Length - 1) {
69+
Write-Host " $buildFlag"
70+
}
71+
else {
72+
Write-Host " $buildFlag ``"
73+
}
74+
$i++
75+
}
76+
}
77+
78+
Write-Host "Running: go build ``"
79+
PrintFlags -flags $buildFlags
80+
go build @buildFlags
81+
if ($LASTEXITCODE) {
82+
Write-Host "Error running go build"
83+
exit $LASTEXITCODE
84+
}
85+
86+
Write-Host "go build succeeded"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
capabilities:
2+
- custom-commands
3+
- metadata
4+
description: Execute scripts and commands with Azure Developer CLI context, environment variables, and Azure Key Vault secret resolution.
5+
displayName: Exec
6+
id: microsoft.azd.exec
7+
language: go
8+
namespace: exec
9+
requiredAzdVersion: ">1.23.6"
10+
usage: azd exec <command> [options]
11+
version: 0.5.0

0 commit comments

Comments
 (0)