Skip to content

Commit 50639b7

Browse files
committed
fix(ci): add cross-platform support for badge update action
Split badge update logic into platform-specific scripts to resolve compatibility issues on macOS and Windows runners. Changes: - Add update-badge-unix.sh for Linux/macOS with timestamp fallbacks - Add update-badge-win.ps1 for Windows with PowerShell native APIs - Update action.yml to conditionally route to platform-specific scripts - Fix macOS date command incompatibility (no %N nanosecond support) - Fix Windows uuidgen missing command error - Use PowerShell native HMAC-SHA256 instead of openssl on Windows - Add graceful fallbacks for macOS without GNU date (gdate) Fixes: - macOS: "Invalid timestamp format: 2025-10-04T20:06:13.3NZ" - Windows: "uuidgen: command not found" (exit code 127)
1 parent e72fb55 commit 50639b7

File tree

4 files changed

+282
-104
lines changed

4 files changed

+282
-104
lines changed

.github/actions/update-test-badge/action.yml

Lines changed: 38 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -42,107 +42,42 @@ inputs:
4242
runs:
4343
using: "composite"
4444
steps:
45-
- name: "Post Test Results to BadgeSmith API"
45+
- name: "Update Badge (Unix/macOS)"
46+
if: runner.os != 'Windows'
4647
shell: bash
47-
run: |
48-
# Extract owner and repo from repository input
49-
IFS='/' read -ra REPO_PARTS <<< "${{ inputs.repository }}"
50-
OWNER="${REPO_PARTS[0]}"
51-
REPO="${REPO_PARTS[1]}"
52-
53-
# Normalize platform name
54-
PLATFORM_LOWER=$(echo "${{ inputs.platform }}" | tr '[:upper:]' '[:lower:]')
55-
56-
# Extract branch from GitHub context
57-
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
58-
BRANCH="${{ github.head_ref }}"
59-
else
60-
BRANCH="${{ github.ref_name }}"
61-
fi
62-
63-
# Calculate totals
64-
TOTAL=$((${{ inputs.test_passed }} + ${{ inputs.test_failed }} + ${{ inputs.test_skipped }}))
65-
66-
# Create JSON payload for BadgeSmith API
67-
cat > test-results.json << EOF
68-
{
69-
"platform": "${{ inputs.platform }}",
70-
"passed": ${{ inputs.test_passed }},
71-
"failed": ${{ inputs.test_failed }},
72-
"skipped": ${{ inputs.test_skipped }},
73-
"total": ${TOTAL},
74-
"url_html": "${{ inputs.test_url_html }}",
75-
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
76-
"commit": "${{ inputs.commit_sha }}",
77-
"run_id": "${{ inputs.run_id }}",
78-
"workflow_run_url": "${{ inputs.server_url }}/${{ inputs.repository }}/actions/runs/${{ inputs.run_id }}"
79-
}
80-
EOF
81-
82-
echo "📊 Generated test results JSON for ${{ inputs.platform }}:"
83-
cat test-results.json | jq '.' 2>/dev/null || cat test-results.json
84-
85-
# Prepare HMAC authentication
86-
PAYLOAD_JSON=$(cat test-results.json)
87-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ")
88-
NONCE=$(uuidgen | tr -d '-' | tr '[:upper:]' '[:lower:]')
89-
90-
# Compute HMAC-SHA256 signature
91-
SIGNATURE="sha256=$(echo -n "$PAYLOAD_JSON" | openssl dgst -sha256 -hmac "${{ inputs.hmac_secret }}" -binary | xxd -p -c 256)"
92-
93-
# Build BadgeSmith API URL
94-
API_URL="https://${{ inputs.api_domain }}/tests/results/${PLATFORM_LOWER}/${OWNER}/${REPO}/${BRANCH}"
95-
96-
echo "🚀 Posting to BadgeSmith API: ${API_URL}"
97-
98-
# Send request to BadgeSmith API
99-
HTTP_CODE=$(curl -s -w "%{http_code}" -o response.tmp \
100-
-X POST "${API_URL}" \
101-
-H "Content-Type: application/json" \
102-
-H "X-Signature: ${SIGNATURE}" \
103-
-H "X-Timestamp: ${TIMESTAMP}" \
104-
-H "X-Nonce: ${NONCE}" \
105-
-d "$PAYLOAD_JSON")
106-
107-
RESPONSE_BODY=$(cat response.tmp)
108-
rm -f response.tmp
109-
110-
if [[ "$HTTP_CODE" -ge 200 && "$HTTP_CODE" -lt 300 ]]; then
111-
echo "✅ Successfully posted test results to BadgeSmith API (HTTP $HTTP_CODE)"
112-
echo "Response:"
113-
echo "$RESPONSE_BODY" | jq . 2>/dev/null || echo "$RESPONSE_BODY"
114-
else
115-
echo "⚠️ Failed to post test results to BadgeSmith API (HTTP $HTTP_CODE)"
116-
echo "Response:"
117-
echo "$RESPONSE_BODY" | jq . 2>/dev/null || echo "$RESPONSE_BODY"
118-
# Don't fail the build for badge update failures
119-
fi
120-
121-
- name: "Display Badge URLs"
122-
shell: bash
123-
run: |
124-
# Extract owner and repo from repository input
125-
IFS='/' read -ra REPO_PARTS <<< "${{ inputs.repository }}"
126-
OWNER="${REPO_PARTS[0]}"
127-
REPO="${REPO_PARTS[1]}"
128-
129-
PLATFORM_LOWER=$(echo "${{ inputs.platform }}" | tr '[:upper:]' '[:lower:]')
130-
131-
# Extract branch from GitHub context
132-
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
133-
BRANCH="${{ github.head_ref }}"
134-
else
135-
BRANCH="${{ github.ref_name }}"
136-
fi
137-
138-
echo "🎯 BadgeSmith URLs for ${{ inputs.platform }}:"
139-
echo ""
140-
echo "**${{ inputs.platform }} Badge:**"
141-
echo "[![Test Results (${{ inputs.platform }})](https://${{ inputs.api_domain }}/badges/tests/${PLATFORM_LOWER}/${OWNER}/${REPO}/${BRANCH})](https://${{ inputs.api_domain }}/redirect/test-results/${PLATFORM_LOWER}/${OWNER}/${REPO}/${BRANCH})"
142-
echo ""
143-
echo "**Raw URLs:**"
144-
echo "- Badge: https://${{ inputs.api_domain }}/badges/tests/${PLATFORM_LOWER}/${OWNER}/${REPO}/${BRANCH}"
145-
echo "- Redirect: https://${{ inputs.api_domain }}/redirect/test-results/${PLATFORM_LOWER}/${OWNER}/${REPO}/${BRANCH}"
146-
echo ""
147-
echo "**API Test:**"
148-
echo "curl \"https://${{ inputs.api_domain }}/badges/tests/${PLATFORM_LOWER}/${OWNER}/${REPO}/${BRANCH}\""
48+
env:
49+
INPUT_PLATFORM: ${{ inputs.platform }}
50+
INPUT_TEST_PASSED: ${{ inputs.test_passed }}
51+
INPUT_TEST_FAILED: ${{ inputs.test_failed }}
52+
INPUT_TEST_SKIPPED: ${{ inputs.test_skipped }}
53+
INPUT_TEST_URL_HTML: ${{ inputs.test_url_html }}
54+
INPUT_COMMIT_SHA: ${{ inputs.commit_sha }}
55+
INPUT_RUN_ID: ${{ inputs.run_id }}
56+
INPUT_REPOSITORY: ${{ inputs.repository }}
57+
INPUT_SERVER_URL: ${{ inputs.server_url }}
58+
INPUT_API_DOMAIN: ${{ inputs.api_domain }}
59+
INPUT_HMAC_SECRET: ${{ inputs.hmac_secret }}
60+
GITHUB_EVENT_NAME: ${{ github.event_name }}
61+
GITHUB_HEAD_REF: ${{ github.head_ref }}
62+
GITHUB_REF_NAME: ${{ github.ref_name }}
63+
run: ${{ github.action_path }}/update-badge-unix.sh
64+
65+
- name: "Update Badge (Windows)"
66+
if: runner.os == 'Windows'
67+
shell: pwsh
68+
env:
69+
INPUT_PLATFORM: ${{ inputs.platform }}
70+
INPUT_TEST_PASSED: ${{ inputs.test_passed }}
71+
INPUT_TEST_FAILED: ${{ inputs.test_failed }}
72+
INPUT_TEST_SKIPPED: ${{ inputs.test_skipped }}
73+
INPUT_TEST_URL_HTML: ${{ inputs.test_url_html }}
74+
INPUT_COMMIT_SHA: ${{ inputs.commit_sha }}
75+
INPUT_RUN_ID: ${{ inputs.run_id }}
76+
INPUT_REPOSITORY: ${{ inputs.repository }}
77+
INPUT_SERVER_URL: ${{ inputs.server_url }}
78+
INPUT_API_DOMAIN: ${{ inputs.api_domain }}
79+
INPUT_HMAC_SECRET: ${{ inputs.hmac_secret }}
80+
GITHUB_EVENT_NAME: ${{ github.event_name }}
81+
GITHUB_HEAD_REF: ${{ github.head_ref }}
82+
GITHUB_REF_NAME: ${{ github.ref_name }}
83+
run: ${{ github.action_path }}/update-badge-win.ps1
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
# update-badge-unix.sh - Post test results to BadgeSmith API (Linux/macOS)
5+
# Usage: Called by action.yml on Linux and macOS runners
6+
7+
echo "🔧 Running BadgeSmith update script for Unix/macOS..."
8+
9+
# Extract owner and repo from repository input
10+
IFS='/' read -ra REPO_PARTS <<< "${INPUT_REPOSITORY}"
11+
OWNER="${REPO_PARTS[0]}"
12+
REPO="${REPO_PARTS[1]}"
13+
14+
# Normalize platform name
15+
PLATFORM_LOWER=$(echo "${INPUT_PLATFORM}" | tr '[:upper:]' '[:lower:]')
16+
17+
# Extract branch from GitHub context
18+
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
19+
BRANCH="${GITHUB_HEAD_REF}"
20+
else
21+
BRANCH="${GITHUB_REF_NAME}"
22+
fi
23+
24+
# Calculate totals
25+
TOTAL=$((INPUT_TEST_PASSED + INPUT_TEST_FAILED + INPUT_TEST_SKIPPED))
26+
27+
# Generate timestamp - macOS compatible (no milliseconds)
28+
if [[ "$OSTYPE" == "darwin"* ]]; then
29+
# macOS: use gdate if available, otherwise fall back to seconds precision
30+
if command -v gdate &> /dev/null; then
31+
TIMESTAMP=$(gdate -u +"%Y-%m-%dT%H:%M:%S.%3NZ")
32+
else
33+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
34+
fi
35+
else
36+
# Linux: GNU date supports %N
37+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ")
38+
fi
39+
40+
# Generate UUID - compatible with both Linux and macOS
41+
if command -v uuidgen &> /dev/null; then
42+
NONCE=$(uuidgen | tr -d '-' | tr '[:upper:]' '[:lower:]')
43+
else
44+
# Fallback: generate from /dev/urandom
45+
NONCE=$(cat /dev/urandom | LC_ALL=C tr -dc 'a-f0-9' | head -c 32)
46+
fi
47+
48+
# Create JSON payload for BadgeSmith API
49+
cat > test-results.json << EOF
50+
{
51+
"platform": "${INPUT_PLATFORM}",
52+
"passed": ${INPUT_TEST_PASSED},
53+
"failed": ${INPUT_TEST_FAILED},
54+
"skipped": ${INPUT_TEST_SKIPPED},
55+
"total": ${TOTAL},
56+
"url_html": "${INPUT_TEST_URL_HTML}",
57+
"timestamp": "${TIMESTAMP}",
58+
"commit": "${INPUT_COMMIT_SHA}",
59+
"run_id": "${INPUT_RUN_ID}",
60+
"workflow_run_url": "${INPUT_SERVER_URL}/${INPUT_REPOSITORY}/actions/runs/${INPUT_RUN_ID}"
61+
}
62+
EOF
63+
64+
echo "📊 Generated test results JSON for ${INPUT_PLATFORM}:"
65+
cat test-results.json | jq '.' 2>/dev/null || cat test-results.json
66+
67+
# Prepare HMAC authentication
68+
PAYLOAD_JSON=$(cat test-results.json)
69+
70+
# Compute HMAC-SHA256 signature
71+
SIGNATURE="sha256=$(echo -n "$PAYLOAD_JSON" | openssl dgst -sha256 -hmac "${INPUT_HMAC_SECRET}" -binary | xxd -p -c 256)"
72+
73+
# Build BadgeSmith API URL
74+
API_URL="https://${INPUT_API_DOMAIN}/tests/results/${PLATFORM_LOWER}/${OWNER}/${REPO}/${BRANCH}"
75+
76+
echo "🚀 Posting to BadgeSmith API: ${API_URL}"
77+
echo "📅 Timestamp: ${TIMESTAMP}"
78+
echo "🔑 Nonce: ${NONCE}"
79+
80+
# Send request to BadgeSmith API
81+
HTTP_CODE=$(curl -s -w "%{http_code}" -o response.tmp \
82+
-X POST "${API_URL}" \
83+
-H "Content-Type: application/json" \
84+
-H "X-Signature: ${SIGNATURE}" \
85+
-H "X-Timestamp: ${TIMESTAMP}" \
86+
-H "X-Nonce: ${NONCE}" \
87+
-d "$PAYLOAD_JSON")
88+
89+
RESPONSE_BODY=$(cat response.tmp)
90+
rm -f response.tmp
91+
92+
if [[ "$HTTP_CODE" -ge 200 && "$HTTP_CODE" -lt 300 ]]; then
93+
echo "✅ Successfully posted test results to BadgeSmith API (HTTP $HTTP_CODE)"
94+
echo "Response:"
95+
echo "$RESPONSE_BODY" | jq . 2>/dev/null || echo "$RESPONSE_BODY"
96+
else
97+
echo "⚠️ Failed to post test results to BadgeSmith API (HTTP $HTTP_CODE)"
98+
echo "Response:"
99+
echo "$RESPONSE_BODY" | jq . 2>/dev/null || echo "$RESPONSE_BODY"
100+
# Don't fail the build for badge update failures
101+
fi
102+
103+
# Display badge URLs
104+
echo ""
105+
echo "🎯 BadgeSmith URLs for ${INPUT_PLATFORM}:"
106+
echo ""
107+
echo "**${INPUT_PLATFORM} Badge:**"
108+
echo "[![Test Results (${INPUT_PLATFORM})](https://${INPUT_API_DOMAIN}/badges/tests/${PLATFORM_LOWER}/${OWNER}/${REPO}/${BRANCH})](https://${INPUT_API_DOMAIN}/redirect/test-results/${PLATFORM_LOWER}/${OWNER}/${REPO}/${BRANCH})"
109+
echo ""
110+
echo "**Raw URLs:**"
111+
echo "- Badge: https://${INPUT_API_DOMAIN}/badges/tests/${PLATFORM_LOWER}/${OWNER}/${REPO}/${BRANCH}"
112+
echo "- Redirect: https://${INPUT_API_DOMAIN}/redirect/test-results/${PLATFORM_LOWER}/${OWNER}/${REPO}/${BRANCH}"
113+
echo ""
114+
echo "**API Test:**"
115+
echo "curl \"https://${INPUT_API_DOMAIN}/badges/tests/${PLATFORM_LOWER}/${OWNER}/${REPO}/${BRANCH}\""
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/usr/bin/env pwsh
2+
# update-badge-win.ps1 - Post test results to BadgeSmith API (Windows)
3+
# Usage: Called by action.yml on Windows runners
4+
5+
$ErrorActionPreference = "Continue" # Don't fail build on badge update failures
6+
7+
Write-Host "🔧 Running BadgeSmith update script for Windows..." -ForegroundColor Cyan
8+
9+
# Extract owner and repo from repository input
10+
$repoParts = $env:INPUT_REPOSITORY -split '/'
11+
$owner = $repoParts[0]
12+
$repo = $repoParts[1]
13+
14+
# Normalize platform name
15+
$platformLower = $env:INPUT_PLATFORM.ToLower()
16+
17+
# Extract branch from GitHub context
18+
if ($env:GITHUB_EVENT_NAME -eq "pull_request") {
19+
$branch = $env:GITHUB_HEAD_REF
20+
} else {
21+
$branch = $env:GITHUB_REF_NAME
22+
}
23+
24+
# Calculate totals
25+
$total = [int]$env:INPUT_TEST_PASSED + [int]$env:INPUT_TEST_FAILED + [int]$env:INPUT_TEST_SKIPPED
26+
27+
# Generate timestamp - ISO 8601 with milliseconds
28+
$timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
29+
30+
# Generate UUID/NONCE
31+
$nonce = [guid]::NewGuid().ToString("N")
32+
33+
# Create JSON payload for BadgeSmith API
34+
$payload = @{
35+
platform = $env:INPUT_PLATFORM
36+
passed = [int]$env:INPUT_TEST_PASSED
37+
failed = [int]$env:INPUT_TEST_FAILED
38+
skipped = [int]$env:INPUT_TEST_SKIPPED
39+
total = $total
40+
url_html = $env:INPUT_TEST_URL_HTML
41+
timestamp = $timestamp
42+
commit = $env:INPUT_COMMIT_SHA
43+
run_id = $env:INPUT_RUN_ID
44+
workflow_run_url = "$($env:INPUT_SERVER_URL)/$($env:INPUT_REPOSITORY)/actions/runs/$($env:INPUT_RUN_ID)"
45+
} | ConvertTo-Json -Compress
46+
47+
Write-Host "📊 Generated test results JSON for $($env:INPUT_PLATFORM):" -ForegroundColor Yellow
48+
Write-Host $payload
49+
50+
# Compute HMAC-SHA256 signature
51+
$hmac = New-Object System.Security.Cryptography.HMACSHA256
52+
$hmac.Key = [System.Text.Encoding]::UTF8.GetBytes($env:INPUT_HMAC_SECRET)
53+
$payloadBytes = [System.Text.Encoding]::UTF8.GetBytes($payload)
54+
$hashBytes = $hmac.ComputeHash($payloadBytes)
55+
$hashHex = [System.BitConverter]::ToString($hashBytes).Replace("-", "").ToLower()
56+
$signature = "sha256=$hashHex"
57+
58+
# Build BadgeSmith API URL
59+
$apiUrl = "https://$($env:INPUT_API_DOMAIN)/tests/results/$platformLower/$owner/$repo/$branch"
60+
61+
Write-Host "🚀 Posting to BadgeSmith API: $apiUrl" -ForegroundColor Green
62+
Write-Host "📅 Timestamp: $timestamp" -ForegroundColor Gray
63+
Write-Host "🔑 Nonce: $nonce" -ForegroundColor Gray
64+
65+
# Send request to BadgeSmith API
66+
try {
67+
$headers = @{
68+
"Content-Type" = "application/json"
69+
"X-Signature" = $signature
70+
"X-Timestamp" = $timestamp
71+
"X-Nonce" = $nonce
72+
}
73+
74+
$response = Invoke-WebRequest `
75+
-Uri $apiUrl `
76+
-Method POST `
77+
-Headers $headers `
78+
-Body $payload `
79+
-UseBasicParsing
80+
81+
$httpCode = $response.StatusCode
82+
$responseBody = $response.Content
83+
84+
if ($httpCode -ge 200 -and $httpCode -lt 300) {
85+
Write-Host "✅ Successfully posted test results to BadgeSmith API (HTTP $httpCode)" -ForegroundColor Green
86+
Write-Host "Response:" -ForegroundColor Gray
87+
try {
88+
$responseBody | ConvertFrom-Json | ConvertTo-Json -Depth 10 | Write-Host
89+
} catch {
90+
Write-Host $responseBody
91+
}
92+
}
93+
} catch {
94+
$httpCode = $_.Exception.Response.StatusCode.Value__
95+
$responseBody = ""
96+
97+
try {
98+
$stream = $_.Exception.Response.GetResponseStream()
99+
$reader = New-Object System.IO.StreamReader($stream)
100+
$responseBody = $reader.ReadToEnd()
101+
$reader.Close()
102+
} catch {
103+
$responseBody = $_.Exception.Message
104+
}
105+
106+
Write-Host "⚠️ Failed to post test results to BadgeSmith API (HTTP $httpCode)" -ForegroundColor Yellow
107+
Write-Host "Response:" -ForegroundColor Gray
108+
try {
109+
$responseBody | ConvertFrom-Json | ConvertTo-Json -Depth 10 | Write-Host
110+
} catch {
111+
Write-Host $responseBody
112+
}
113+
# Don't fail the build for badge update failures
114+
}
115+
116+
# Display badge URLs
117+
Write-Host ""
118+
Write-Host "🎯 BadgeSmith URLs for $($env:INPUT_PLATFORM):" -ForegroundColor Cyan
119+
Write-Host ""
120+
Write-Host "**$($env:INPUT_PLATFORM) Badge:**"
121+
Write-Host "[![Test Results ($($env:INPUT_PLATFORM))](https://$($env:INPUT_API_DOMAIN)/badges/tests/$platformLower/$owner/$repo/$branch)](https://$($env:INPUT_API_DOMAIN)/redirect/test-results/$platformLower/$owner/$repo/$branch)"
122+
Write-Host ""
123+
Write-Host "**Raw URLs:**"
124+
Write-Host "- Badge: https://$($env:INPUT_API_DOMAIN)/badges/tests/$platformLower/$owner/$repo/$branch"
125+
Write-Host "- Redirect: https://$($env:INPUT_API_DOMAIN)/redirect/test-results/$platformLower/$owner/$repo/$branch"
126+
Write-Host ""
127+
Write-Host "**API Test:**"
128+
Write-Host "curl `"https://$($env:INPUT_API_DOMAIN)/badges/tests/$platformLower/$owner/$repo/$branch`""

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# .NET Aspire Integrations for LocalStack
22

3-
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![NuGet Version](https://img.shields.io/endpoint?url=https%3A%2F%2Fapi.localstackfor.net%2Fbadges%2Fpackages%2Fnuget%2FLocalStack.Aspire.Hosting%3Fprerelease%3Dtrue)](https://www.nuget.org/packages/LocalStack.Aspire.Hosting) [![Github Packages](https://img.shields.io/endpoint?url=https%3A%2F%2Fapi.localstackfor.net%2Fbadges%2Fpackages%2Fgithub%2Flocalstack-dotnet%2FLocalStack.Aspire.Hosting%3Fprerelease%3Dtrue)](https://github.com/localstack-dotnet/dotnet-aspire-for-localstack/pkgs/nuget/LocalStack.Aspire.Hosting) [![CI/CD Pipeline](https://github.com/localstack-dotnet/dotnet-aspire-for-localstack/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/localstack-dotnet/dotnet-aspire-for-localstack/actions/workflows/ci-cd.yml) [![Security](https://github.com/localstack-dotnet/dotnet-aspire-for-localstack/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/localstack-dotnet/dotnet-aspire-for-localstack/actions/workflows/github-code-scanning/codeql) [![Linux Tests](https://img.shields.io/endpoint?url=https%3A%2F%2Fapi.localstackfor.net%2Fbadges%2Ftests%2Flinux%2Flocalstack-dotnet%2Fdotnet-aspire-for-localstack%2Fmaster)](https://api.localstackfor.net/redirect/test-results/linux?package=LocalStack.Aspire.Hosting)
3+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![NuGet Version](https://img.shields.io/endpoint?url=https%3A%2F%2Fapi.localstackfor.net%2Fbadges%2Fpackages%2Fnuget%2FLocalStack.Aspire.Hosting%3Fprerelease%3Dtrue)](https://www.nuget.org/packages/LocalStack.Aspire.Hosting) [![Github Packages](https://img.shields.io/endpoint?url=https%3A%2F%2Fapi.localstackfor.net%2Fbadges%2Fpackages%2Fgithub%2Flocalstack-dotnet%2FLocalStack.Aspire.Hosting%3Fprerelease%3Dtrue)](https://github.com/localstack-dotnet/dotnet-aspire-for-localstack/pkgs/nuget/LocalStack.Aspire.Hosting) [![CI/CD Pipeline](https://github.com/localstack-dotnet/dotnet-aspire-for-localstack/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/localstack-dotnet/dotnet-aspire-for-localstack/actions/workflows/ci-cd.yml) [![Security](https://github.com/localstack-dotnet/dotnet-aspire-for-localstack/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/localstack-dotnet/dotnet-aspire-for-localstack/actions/workflows/github-code-scanning/codeql) [![Linux Tests](https://img.shields.io/endpoint?url=https%3A%2F%2Fapi.localstackfor.net%2Fbadges%2Ftests%2Flinux%2Flocalstack-dotnet%2Fdotnet-aspire-for-localstack%2Fmaster)](https://api.localstackfor.net/redirect/test-results/linux/localstack-dotnet/dotnet-aspire-for-localstack/master)
44

55
A .NET Aspire hosting integration for [LocalStack](https://localstack.cloud/) that enables local development and testing of cloud applications using AWS services. This package extends the official [AWS integrations for .NET Aspire](https://github.com/aws/integrations-on-dotnet-aspire-for-aws) to provide LocalStack-specific functionality.
66

0 commit comments

Comments
 (0)