From 3f1905a19fb622e5c4a7cf3a97389cfed5bdfab2 Mon Sep 17 00:00:00 2001 From: Daniel van der Ploeg Date: Fri, 15 Aug 2025 08:47:42 +0930 Subject: [PATCH 1/6] DO-1743: add comprehensive Docker ECR deployment workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Multi-platform Docker builds (linux/amd64, linux/arm64) • ECR authentication and repository management with lifecycle policies • Container vulnerability scanning with Trivy and configurable security thresholds • Container image signing and attestation with cosign • Smart tagging strategies (latest, semantic, branch, custom) • Build cache optimization with registry and inline caching • Multi-stage build support with build arguments • Registry cleanup and automated image retention • Comprehensive input validation and security gates • Detailed deployment summary and artifact management 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .github/workflows/docker-ecr-deploy.yml | 629 ++++++++++++++++++++++++ README.md | 129 +++++ 2 files changed, 758 insertions(+) create mode 100644 .github/workflows/docker-ecr-deploy.yml diff --git a/.github/workflows/docker-ecr-deploy.yml b/.github/workflows/docker-ecr-deploy.yml new file mode 100644 index 0000000..aec7093 --- /dev/null +++ b/.github/workflows/docker-ecr-deploy.yml @@ -0,0 +1,629 @@ +name: 🐳 Docker ECR Deployment + +on: + workflow_call: + inputs: + # Core Configuration + aws-region: + description: "AWS region for ECR registry" + type: string + required: false + default: "ap-southeast-2" + ecr-repository: + description: "ECR repository name (required)" + type: string + required: true + dockerfile-path: + description: "Path to Dockerfile" + type: string + required: false + default: "Dockerfile" + build-context: + description: "Docker build context path" + type: string + required: false + default: "." + + # Platform and Build Configuration + platforms: + description: "Target platforms for multi-platform builds" + type: string + required: false + default: "linux/amd64,linux/arm64" + push-to-registry: + description: "Push built images to ECR registry" + type: boolean + required: false + default: true + + # Security and Scanning + vulnerability-scan: + description: "Enable container vulnerability scanning" + type: boolean + required: false + default: true + security-threshold: + description: "Security vulnerability threshold (CRITICAL/HIGH/MEDIUM/LOW)" + type: string + required: false + default: "HIGH" + + # Tagging Strategy + tag-strategy: + description: "Image tagging strategy (latest/semantic/branch/custom)" + type: string + required: false + default: "latest" + custom-tags: + description: "Custom tags (comma-separated) when using custom strategy" + type: string + required: false + default: "" + + # Build Optimization + cache-from: + description: "Cache sources for build optimization (comma-separated)" + type: string + required: false + default: "" + build-args: + description: "Docker build arguments as JSON object" + type: string + required: false + default: "{}" + target-stage: + description: "Target build stage for multi-stage Dockerfiles" + type: string + required: false + default: "" + + # Registry Management + cleanup-old-images: + description: "Clean up old images from ECR registry" + type: boolean + required: false + default: false + retention-count: + description: "Number of images to retain when cleaning up" + type: string + required: false + default: "10" + + # Container Signing + enable-signing: + description: "Enable container image signing with cosign" + type: boolean + required: false + default: false + + # Advanced Configuration + debug: + description: "Enable verbose logging and debug output" + type: boolean + required: false + default: false + + secrets: + aws-access-key-id: + description: "AWS access key ID" + required: true + aws-secret-access-key: + description: "AWS secret access key" + required: true + container-signing-key: + description: "Private key for container signing (optional)" + required: false + + outputs: + image-uri: + description: "Full URI of the built container image" + value: ${{ jobs.build.outputs.image-uri }} + image-digest: + description: "SHA256 digest of the built image" + value: ${{ jobs.build.outputs.image-digest }} + image-tags: + description: "Applied image tags as JSON array" + value: ${{ jobs.build.outputs.image-tags }} + vulnerability-report: + description: "Container vulnerability scan results" + value: ${{ jobs.scan.outputs.vulnerability-report }} + +jobs: + # Validate inputs and prepare build configuration + prepare: + name: 🔍 Prepare Docker Build + runs-on: ubuntu-latest + outputs: + image-tags: ${{ steps.tag-config.outputs.tags }} + build-args: ${{ steps.build-config.outputs.args }} + cache-config: ${{ steps.cache-config.outputs.setup }} + platforms: ${{ steps.platform-config.outputs.platforms }} + security-scan: ${{ steps.security-config.outputs.enabled }} + steps: + - name: Validate required inputs + run: | + echo "🔍 Validating Docker build configuration..." + + if [ -z "${{ inputs.ecr-repository }}" ]; then + echo "❌ Error: ecr-repository is required" + exit 1 + fi + + # Validate security threshold + case "${{ inputs.security-threshold }}" in + CRITICAL|HIGH|MEDIUM|LOW) + echo "✅ Security threshold: ${{ inputs.security-threshold }}" + ;; + *) + echo "❌ Error: security-threshold must be one of: CRITICAL, HIGH, MEDIUM, LOW" + exit 1 + ;; + esac + + # Validate tag strategy + case "${{ inputs.tag-strategy }}" in + latest|semantic|branch|custom) + echo "✅ Tag strategy: ${{ inputs.tag-strategy }}" + ;; + *) + echo "❌ Error: tag-strategy must be one of: latest, semantic, branch, custom" + exit 1 + ;; + esac + + # Validate build args JSON if provided + if [ "${{ inputs.build-args }}" != "{}" ]; then + echo '${{ inputs.build-args }}' | jq . > /dev/null + if [ $? -ne 0 ]; then + echo "❌ Error: build-args must be valid JSON" + exit 1 + fi + fi + + echo "✅ All inputs validated successfully" + + - name: Configure image tags + id: tag-config + run: | + echo "🏷️ Configuring image tags..." + + tags="" + case "${{ inputs.tag-strategy }}" in + latest) + tags="latest" + if [ "${{ github.ref_type }}" = "tag" ]; then + tags="$tags,${{ github.ref_name }}" + fi + ;; + semantic) + if [ "${{ github.ref_type }}" = "tag" ]; then + tag_name="${{ github.ref_name }}" + tags="$tag_name" + # Extract semantic version components + if [[ $tag_name =~ ^v?([0-9]+)\.([0-9]+)\.([0-9]+) ]]; then + major="${BASH_REMATCH[1]}" + minor="${BASH_REMATCH[2]}" + patch="${BASH_REMATCH[3]}" + tags="$tags,$major,$major.$minor,$major.$minor.$patch" + fi + else + tags="latest" + fi + ;; + branch) + branch_name=$(echo "${{ github.ref_name }}" | sed 's/[^a-zA-Z0-9.-]/-/g') + tags="$branch_name" + if [ "${{ github.ref_name }}" = "main" ] || [ "${{ github.ref_name }}" = "master" ]; then + tags="$tags,latest" + fi + ;; + custom) + if [ -n "${{ inputs.custom-tags }}" ]; then + tags="${{ inputs.custom-tags }}" + else + echo "❌ Error: custom-tags must be provided when using custom strategy" + exit 1 + fi + ;; + esac + + # Add commit SHA tag + short_sha=$(echo "${{ github.sha }}" | cut -c1-7) + tags="$tags,sha-$short_sha" + + echo "tags=$tags" >> $GITHUB_OUTPUT + echo "✅ Tags configured: $tags" + + - name: Configure build arguments + id: build-config + run: | + echo "⚙️ Configuring build arguments..." + + build_args="" + + # Add default build args + build_args="$build_args --build-arg BUILDKIT_INLINE_CACHE=1" + build_args="$build_args --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" + build_args="$build_args --build-arg VCS_REF=${{ github.sha }}" + build_args="$build_args --build-arg VERSION=${{ github.ref_name }}" + + # Add custom build args + if [ "${{ inputs.build-args }}" != "{}" ]; then + echo '${{ inputs.build-args }}' | jq -r 'to_entries[] | "--build-arg \(.key)=\(.value)"' | while read -r arg; do + build_args="$build_args $arg" + done + fi + + echo "args=$build_args" >> $GITHUB_OUTPUT + echo "✅ Build arguments configured" + + - name: Configure cache settings + id: cache-config + run: | + echo "💾 Configuring build cache..." + + cache_from="" + if [ -n "${{ inputs.cache-from }}" ]; then + # Convert comma-separated cache sources to buildx format + IFS=',' read -ra CACHE_SOURCES <<< "${{ inputs.cache-from }}" + for source in "${CACHE_SOURCES[@]}"; do + cache_from="$cache_from --cache-from type=registry,ref=$source" + done + fi + + # Add ECR cache source + ecr_uri="${{ inputs.ecr-repository }}:buildcache" + cache_from="$cache_from --cache-from type=registry,ref=$ecr_uri" + cache_to="--cache-to type=registry,ref=$ecr_uri,mode=max" + + echo "setup=$cache_from $cache_to" >> $GITHUB_OUTPUT + echo "✅ Cache configuration prepared" + + - name: Configure platforms + id: platform-config + run: | + echo "🏗️ Configuring build platforms..." + + platforms="${{ inputs.platforms }}" + + # Validate platforms + IFS=',' read -ra PLATFORM_LIST <<< "$platforms" + for platform in "${PLATFORM_LIST[@]}"; do + case "$platform" in + linux/amd64|linux/arm64|linux/arm/v7|linux/arm/v8) + echo "✅ Platform supported: $platform" + ;; + *) + echo "⚠️ Warning: Unusual platform: $platform" + ;; + esac + done + + echo "platforms=$platforms" >> $GITHUB_OUTPUT + echo "✅ Platforms configured: $platforms" + + - name: Configure security scanning + id: security-config + run: | + echo "🔒 Configuring security settings..." + + scan_enabled="${{ inputs.vulnerability-scan }}" + + if [ "$scan_enabled" = "true" ]; then + echo "enabled=true" >> $GITHUB_OUTPUT + echo "✅ Vulnerability scanning enabled (threshold: ${{ inputs.security-threshold }})" + else + echo "enabled=false" >> $GITHUB_OUTPUT + echo "ℹ️ Vulnerability scanning disabled" + fi + + # Build and push Docker image + build: + name: 🏗️ Build & Push Docker Image + runs-on: ubuntu-latest + needs: prepare + outputs: + image-uri: ${{ steps.build.outputs.image-uri }} + image-digest: ${{ steps.build.outputs.digest }} + image-tags: ${{ steps.build.outputs.tags }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + platforms: ${{ needs.prepare.outputs.platforms }} + driver-opts: | + network=host + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.aws-access-key-id }} + aws-secret-access-key: ${{ secrets.aws-secret-access-key }} + aws-region: ${{ inputs.aws-region }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + with: + mask-password: 'true' + + - name: Create ECR repository if needed + run: | + echo "🗄️ Ensuring ECR repository exists..." + + aws ecr describe-repositories --repository-names ${{ inputs.ecr-repository }} --region ${{ inputs.aws-region }} 2>/dev/null || { + echo "📦 Creating ECR repository: ${{ inputs.ecr-repository }}" + aws ecr create-repository \ + --repository-name ${{ inputs.ecr-repository }} \ + --region ${{ inputs.aws-region }} \ + --image-scanning-configuration scanOnPush=true \ + --encryption-configuration encryptionType=AES256 + + # Set lifecycle policy + aws ecr put-lifecycle-policy \ + --repository-name ${{ inputs.ecr-repository }} \ + --region ${{ inputs.aws-region }} \ + --lifecycle-policy-text '{ + "rules": [ + { + "rulePriority": 1, + "description": "Keep last 30 images", + "selection": { + "tagStatus": "any", + "countType": "imageCountMoreThan", + "countNumber": 30 + }, + "action": { + "type": "expire" + } + } + ] + }' + } + + echo "✅ ECR repository ready" + + - name: Build and push Docker image + id: build + uses: docker/build-push-action@v5 + with: + context: ${{ inputs.build-context }} + file: ${{ inputs.dockerfile-path }} + platforms: ${{ needs.prepare.outputs.platforms }} + push: ${{ inputs.push-to-registry }} + tags: | + ${{ steps.login-ecr.outputs.registry }}/${{ inputs.ecr-repository }}:${{ needs.prepare.outputs.image-tags }} + target: ${{ inputs.target-stage }} + cache-from: ${{ needs.prepare.outputs.cache-config }} + cache-to: type=inline + provenance: true + sbom: true + outputs: type=image,name=${{ steps.login-ecr.outputs.registry }}/${{ inputs.ecr-repository }},push=${{ inputs.push-to-registry }} + + - name: Extract build metadata + id: metadata + run: | + echo "📋 Extracting build metadata..." + + registry="${{ steps.login-ecr.outputs.registry }}" + repository="${{ inputs.ecr-repository }}" + + # Get image digest from build output + digest="${{ steps.build.outputs.digest }}" + + # Construct image URI + image_uri="$registry/$repository@$digest" + + # Format tags as JSON array + tags_array=$(echo "${{ needs.prepare.outputs.image-tags }}" | sed 's/,/","/g' | sed 's/^/["/' | sed 's/$/"]/') + + echo "image-uri=$image_uri" >> $GITHUB_OUTPUT + echo "digest=$digest" >> $GITHUB_OUTPUT + echo "tags=$tags_array" >> $GITHUB_OUTPUT + + echo "✅ Image built: $image_uri" + echo "🏷️ Tags applied: ${{ needs.prepare.outputs.image-tags }}" + + - name: Sign container image + if: inputs.enable-signing == true && secrets.container-signing-key != '' + uses: sigstore/cosign-installer@v3 + + - name: Sign the container image + if: inputs.enable-signing == true && secrets.container-signing-key != '' + run: | + echo "✍️ Signing container image..." + + echo "${{ secrets.container-signing-key }}" > cosign.key + + cosign sign --key cosign.key \ + ${{ steps.metadata.outputs.image-uri }} + + rm cosign.key + echo "✅ Image signed successfully" + + # Vulnerability scanning + scan: + name: 🔍 Security Vulnerability Scan + runs-on: ubuntu-latest + needs: [prepare, build] + if: needs.prepare.outputs.security-scan == 'true' && needs.build.result == 'success' + outputs: + vulnerability-report: ${{ steps.scan.outputs.report }} + security-status: ${{ steps.scan.outputs.status }} + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.aws-access-key-id }} + aws-secret-access-key: ${{ secrets.aws-secret-access-key }} + aws-region: ${{ inputs.aws-region }} + + - name: Run Trivy vulnerability scanner + id: scan + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ needs.build.outputs.image-uri }} + format: 'sarif' + output: 'trivy-results.sarif' + severity: 'CRITICAL,HIGH,MEDIUM,LOW' + + - name: Analyze vulnerability results + id: analyze + run: | + echo "🔍 Analyzing vulnerability scan results..." + + # Parse SARIF results + critical_count=$(jq '[.runs[].results[] | select(.level == "error")] | length' trivy-results.sarif) + high_count=$(jq '[.runs[].results[] | select(.level == "warning")] | length' trivy-results.sarif) + medium_count=$(jq '[.runs[].results[] | select(.level == "note")] | length' trivy-results.sarif) + + echo "🔴 Critical vulnerabilities: $critical_count" + echo "🟠 High vulnerabilities: $high_count" + echo "🟡 Medium vulnerabilities: $medium_count" + + # Check against threshold + case "${{ inputs.security-threshold }}" in + CRITICAL) + if [ "$critical_count" -gt 0 ]; then + echo "❌ Critical vulnerabilities found - blocking deployment" + echo "status=failed" >> $GITHUB_OUTPUT + exit 1 + fi + ;; + HIGH) + if [ "$critical_count" -gt 0 ] || [ "$high_count" -gt 0 ]; then + echo "❌ High or critical vulnerabilities found - blocking deployment" + echo "status=failed" >> $GITHUB_OUTPUT + exit 1 + fi + ;; + MEDIUM) + if [ "$critical_count" -gt 0 ] || [ "$high_count" -gt 0 ] || [ "$medium_count" -gt 0 ]; then + echo "❌ Medium, high, or critical vulnerabilities found - blocking deployment" + echo "status=failed" >> $GITHUB_OUTPUT + exit 1 + fi + ;; + LOW) + echo "ℹ️ All vulnerabilities reported for awareness" + ;; + esac + + echo "status=passed" >> $GITHUB_OUTPUT + echo "✅ Vulnerability scan passed security threshold" + + # Create summary report + report=$(jq -c '{ + critical: [.runs[].results[] | select(.level == "error")] | length, + high: [.runs[].results[] | select(.level == "warning")] | length, + medium: [.runs[].results[] | select(.level == "note")] | length, + threshold: "${{ inputs.security-threshold }}", + status: "passed" + }' trivy-results.sarif) + + echo "report=$report" >> $GITHUB_OUTPUT + + - name: Upload Trivy scan results + uses: actions/upload-artifact@v4 + with: + name: trivy-results + path: trivy-results.sarif + retention-days: 30 + + - name: Upload to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: trivy-results.sarif + + # Registry cleanup + cleanup: + name: 🧹 Registry Cleanup + runs-on: ubuntu-latest + needs: [prepare, build, scan] + if: inputs.cleanup-old-images == true && (needs.scan.result == 'success' || needs.scan.result == 'skipped') && needs.build.result == 'success' + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.aws-access-key-id }} + aws-secret-access-key: ${{ secrets.aws-secret-access-key }} + aws-region: ${{ inputs.aws-region }} + + - name: Clean up old images + run: | + echo "🧹 Cleaning up old images from ECR..." + + # Get list of images sorted by pushed date + images=$(aws ecr describe-images \ + --repository-name ${{ inputs.ecr-repository }} \ + --region ${{ inputs.aws-region }} \ + --query 'sort_by(imageDetails, &imagePushedAt)[:-${{ inputs.retention-count }}].imageDigest' \ + --output text) + + if [ -n "$images" ] && [ "$images" != "None" ]; then + echo "🗑️ Found $(echo $images | wc -w) images to delete" + + for digest in $images; do + echo "Deleting image: $digest" + aws ecr batch-delete-image \ + --repository-name ${{ inputs.ecr-repository }} \ + --region ${{ inputs.aws-region }} \ + --image-ids imageDigest=$digest + done + + echo "✅ Cleanup completed" + else + echo "ℹ️ No images to clean up" + fi + + # Deployment summary + summary: + name: 📋 Deployment Summary + runs-on: ubuntu-latest + needs: [prepare, build, scan, cleanup] + if: always() && needs.build.result == 'success' + steps: + - name: Display deployment summary + run: | + echo "📋 Docker ECR Deployment Summary" + echo "==================================" + echo "Repository: ${{ inputs.ecr-repository }}" + echo "Region: ${{ inputs.aws-region }}" + echo "Platforms: ${{ needs.prepare.outputs.platforms }}" + echo "Image URI: ${{ needs.build.outputs.image-uri }}" + echo "Tags: ${{ needs.prepare.outputs.image-tags }}" + echo "" + + if [ "${{ inputs.vulnerability-scan }}" = "true" ]; then + if [ "${{ needs.scan.result }}" = "success" ]; then + echo "🔒 Security scan: ✅ PASSED" + echo "Security threshold: ${{ inputs.security-threshold }}" + else + echo "🔒 Security scan: ❌ FAILED" + fi + else + echo "🔒 Security scan: ⏭️ SKIPPED" + fi + + if [ "${{ inputs.cleanup-old-images }}" = "true" ]; then + if [ "${{ needs.cleanup.result }}" = "success" ]; then + echo "🧹 Registry cleanup: ✅ COMPLETED" + else + echo "🧹 Registry cleanup: ❌ FAILED" + fi + else + echo "🧹 Registry cleanup: ⏭️ SKIPPED" + fi + + if [ "${{ inputs.enable-signing }}" = "true" ]; then + echo "✍️ Image signing: ✅ ENABLED" + else + echo "✍️ Image signing: ⏭️ DISABLED" + fi + + echo "" + echo "🎉 Docker deployment completed successfully!" \ No newline at end of file diff --git a/README.md b/README.md index c551f4d..7a2a31a 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,135 @@ jobs: ``` +### Docker ECR Deployment + +A comprehensive Docker container deployment workflow supporting multi-platform builds, ECR registry management, vulnerability scanning, and container security with build optimization and registry lifecycle management. + +#### **Features** +- **Multi-platform builds**: Support for linux/amd64, linux/arm64, and ARM variants +- **ECR integration**: Automated ECR repository creation and lifecycle management +- **Vulnerability scanning**: Trivy security scanning with configurable thresholds +- **Container signing**: Optional cosign-based image signing and attestation +- **Smart tagging**: Multiple tagging strategies (latest, semantic, branch, custom) +- **Build optimization**: Advanced caching with registry and inline cache support +- **Registry cleanup**: Automated cleanup of old images with retention policies +- **Multi-stage builds**: Support for target build stages and build arguments +- **Security gates**: Configurable vulnerability thresholds blocking insecure deployments + +#### **Inputs** +| Name | Required | Type | Default | Description | +|------|----------|------|---------|-------------| +| **Core Configuration** | +| aws-region | ❌ | string | ap-southeast-2 | AWS region for ECR registry | +| ecr-repository | ✅ | string | | ECR repository name (required) | +| dockerfile-path | ❌ | string | Dockerfile | Path to Dockerfile | +| build-context | ❌ | string | . | Docker build context path | +| **Platform and Build Configuration** | +| platforms | ❌ | string | linux/amd64,linux/arm64 | Target platforms for multi-platform builds | +| push-to-registry | ❌ | boolean | true | Push built images to ECR registry | +| **Security and Scanning** | +| vulnerability-scan | ❌ | boolean | true | Enable container vulnerability scanning | +| security-threshold | ❌ | string | HIGH | Security vulnerability threshold (CRITICAL/HIGH/MEDIUM/LOW) | +| **Tagging Strategy** | +| tag-strategy | ❌ | string | latest | Image tagging strategy (latest/semantic/branch/custom) | +| custom-tags | ❌ | string | | Custom tags (comma-separated) when using custom strategy | +| **Build Optimization** | +| cache-from | ❌ | string | | Cache sources for build optimization (comma-separated) | +| build-args | ❌ | string | {} | Docker build arguments as JSON object | +| target-stage | ❌ | string | | Target build stage for multi-stage Dockerfiles | +| **Registry Management** | +| cleanup-old-images | ❌ | boolean | false | Clean up old images from ECR registry | +| retention-count | ❌ | string | 10 | Number of images to retain when cleaning up | +| **Container Signing** | +| enable-signing | ❌ | boolean | false | Enable container image signing with cosign | +| **Advanced Configuration** | +| debug | ❌ | boolean | false | Enable verbose logging and debug output | + +#### **Secrets** +| Name | Required | Description | +|------|----------|-------------| +| aws-access-key-id | ✅ | AWS access key ID | +| aws-secret-access-key | ✅ | AWS secret access key | +| container-signing-key | ❌ | Private key for container signing (optional) | + +#### **Outputs** +| Name | Description | +|------|-------------| +| image-uri | Full URI of the built container image | +| image-digest | SHA256 digest of the built image | +| image-tags | Applied image tags as JSON array | +| vulnerability-report | Container vulnerability scan results | + +#### **Example Usage** + +**Basic Docker Build and Push:** +```yaml +jobs: + docker-deploy: + uses: aligent/workflows/.github/workflows/docker-ecr-deploy.yml@main + with: + ecr-repository: my-app + dockerfile-path: Dockerfile + secrets: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} +``` + +**Multi-platform with Vulnerability Scanning:** +```yaml +jobs: + secure-deploy: + uses: aligent/workflows/.github/workflows/docker-ecr-deploy.yml@main + with: + ecr-repository: my-secure-app + platforms: "linux/amd64,linux/arm64" + vulnerability-scan: true + security-threshold: "CRITICAL" + tag-strategy: "semantic" + secrets: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} +``` + +**Production with Signing and Cleanup:** +```yaml +jobs: + production-deploy: + uses: aligent/workflows/.github/workflows/docker-ecr-deploy.yml@main + with: + ecr-repository: my-prod-app + tag-strategy: "semantic" + enable-signing: true + cleanup-old-images: true + retention-count: "5" + security-threshold: "HIGH" + aws-region: "us-east-1" + secrets: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + container-signing-key: ${{ secrets.COSIGN_PRIVATE_KEY }} +``` + +**Custom Build with Optimization:** +```yaml +jobs: + optimized-build: + uses: aligent/workflows/.github/workflows/docker-ecr-deploy.yml@main + with: + ecr-repository: my-optimized-app + build-context: "./backend" + dockerfile-path: "./backend/Dockerfile.prod" + target-stage: "production" + build-args: '{"NODE_ENV": "production", "API_VERSION": "v2"}' + cache-from: "my-optimized-app:buildcache,my-base-image:latest" + tag-strategy: "custom" + custom-tags: "latest,v2.1.0,production" + debug: true + secrets: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} +``` + ### Node Pull Request Checks #### **Inputs** From d9ad97b8714069d6db545ac25abc0ba9969fb6de Mon Sep 17 00:00:00 2001 From: Daniel van der Ploeg Date: Fri, 15 Aug 2025 09:39:45 +0930 Subject: [PATCH 2/6] DO-1743: pin third-party GitHub Actions to commit SHAs for security - Pin sigstore/cosign-installer@v3 to commit SHA 398d4b0eeef1380460a10c8013a76f728fb906ac - Pin aquasecurity/trivy-action@master to commit SHA 77137e9dc3ab1b329b7c8a38c2eb7475850a14e8 - Addresses Aikido security recommendations for supply chain attack prevention --- .github/workflows/docker-ecr-deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-ecr-deploy.yml b/.github/workflows/docker-ecr-deploy.yml index aec7093..8cbee35 100644 --- a/.github/workflows/docker-ecr-deploy.yml +++ b/.github/workflows/docker-ecr-deploy.yml @@ -429,7 +429,7 @@ jobs: - name: Sign container image if: inputs.enable-signing == true && secrets.container-signing-key != '' - uses: sigstore/cosign-installer@v3 + uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3 - name: Sign the container image if: inputs.enable-signing == true && secrets.container-signing-key != '' @@ -463,7 +463,7 @@ jobs: - name: Run Trivy vulnerability scanner id: scan - uses: aquasecurity/trivy-action@master + uses: aquasecurity/trivy-action@77137e9dc3ab1b329b7c8a38c2eb7475850a14e8 # master with: image-ref: ${{ needs.build.outputs.image-uri }} format: 'sarif' From 724c7574bbd3d2fecf67898cf1b499b9ac02cc08 Mon Sep 17 00:00:00 2001 From: Daniel van der Ploeg Date: Mon, 15 Sep 2025 15:22:07 +0930 Subject: [PATCH 3/6] chore: remove security scanning this is handled by a third party system --- .github/workflows/docker-ecr-deploy.yml | 153 +----------------------- README.md | 17 +-- 2 files changed, 7 insertions(+), 163 deletions(-) diff --git a/.github/workflows/docker-ecr-deploy.yml b/.github/workflows/docker-ecr-deploy.yml index 8cbee35..f682d05 100644 --- a/.github/workflows/docker-ecr-deploy.yml +++ b/.github/workflows/docker-ecr-deploy.yml @@ -36,18 +36,6 @@ on: required: false default: true - # Security and Scanning - vulnerability-scan: - description: "Enable container vulnerability scanning" - type: boolean - required: false - default: true - security-threshold: - description: "Security vulnerability threshold (CRITICAL/HIGH/MEDIUM/LOW)" - type: string - required: false - default: "HIGH" - # Tagging Strategy tag-strategy: description: "Image tagging strategy (latest/semantic/branch/custom)" @@ -124,9 +112,6 @@ on: image-tags: description: "Applied image tags as JSON array" value: ${{ jobs.build.outputs.image-tags }} - vulnerability-report: - description: "Container vulnerability scan results" - value: ${{ jobs.scan.outputs.vulnerability-report }} jobs: # Validate inputs and prepare build configuration @@ -138,7 +123,6 @@ jobs: build-args: ${{ steps.build-config.outputs.args }} cache-config: ${{ steps.cache-config.outputs.setup }} platforms: ${{ steps.platform-config.outputs.platforms }} - security-scan: ${{ steps.security-config.outputs.enabled }} steps: - name: Validate required inputs run: | @@ -149,17 +133,6 @@ jobs: exit 1 fi - # Validate security threshold - case "${{ inputs.security-threshold }}" in - CRITICAL|HIGH|MEDIUM|LOW) - echo "✅ Security threshold: ${{ inputs.security-threshold }}" - ;; - *) - echo "❌ Error: security-threshold must be one of: CRITICAL, HIGH, MEDIUM, LOW" - exit 1 - ;; - esac - # Validate tag strategy case "${{ inputs.tag-strategy }}" in latest|semantic|branch|custom) @@ -302,20 +275,6 @@ jobs: echo "platforms=$platforms" >> $GITHUB_OUTPUT echo "✅ Platforms configured: $platforms" - - name: Configure security scanning - id: security-config - run: | - echo "🔒 Configuring security settings..." - - scan_enabled="${{ inputs.vulnerability-scan }}" - - if [ "$scan_enabled" = "true" ]; then - echo "enabled=true" >> $GITHUB_OUTPUT - echo "✅ Vulnerability scanning enabled (threshold: ${{ inputs.security-threshold }})" - else - echo "enabled=false" >> $GITHUB_OUTPUT - echo "ℹ️ Vulnerability scanning disabled" - fi # Build and push Docker image build: @@ -444,107 +403,12 @@ jobs: rm cosign.key echo "✅ Image signed successfully" - # Vulnerability scanning - scan: - name: 🔍 Security Vulnerability Scan - runs-on: ubuntu-latest - needs: [prepare, build] - if: needs.prepare.outputs.security-scan == 'true' && needs.build.result == 'success' - outputs: - vulnerability-report: ${{ steps.scan.outputs.report }} - security-status: ${{ steps.scan.outputs.status }} - steps: - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - aws-access-key-id: ${{ secrets.aws-access-key-id }} - aws-secret-access-key: ${{ secrets.aws-secret-access-key }} - aws-region: ${{ inputs.aws-region }} - - - name: Run Trivy vulnerability scanner - id: scan - uses: aquasecurity/trivy-action@77137e9dc3ab1b329b7c8a38c2eb7475850a14e8 # master - with: - image-ref: ${{ needs.build.outputs.image-uri }} - format: 'sarif' - output: 'trivy-results.sarif' - severity: 'CRITICAL,HIGH,MEDIUM,LOW' - - - name: Analyze vulnerability results - id: analyze - run: | - echo "🔍 Analyzing vulnerability scan results..." - - # Parse SARIF results - critical_count=$(jq '[.runs[].results[] | select(.level == "error")] | length' trivy-results.sarif) - high_count=$(jq '[.runs[].results[] | select(.level == "warning")] | length' trivy-results.sarif) - medium_count=$(jq '[.runs[].results[] | select(.level == "note")] | length' trivy-results.sarif) - - echo "🔴 Critical vulnerabilities: $critical_count" - echo "🟠 High vulnerabilities: $high_count" - echo "🟡 Medium vulnerabilities: $medium_count" - - # Check against threshold - case "${{ inputs.security-threshold }}" in - CRITICAL) - if [ "$critical_count" -gt 0 ]; then - echo "❌ Critical vulnerabilities found - blocking deployment" - echo "status=failed" >> $GITHUB_OUTPUT - exit 1 - fi - ;; - HIGH) - if [ "$critical_count" -gt 0 ] || [ "$high_count" -gt 0 ]; then - echo "❌ High or critical vulnerabilities found - blocking deployment" - echo "status=failed" >> $GITHUB_OUTPUT - exit 1 - fi - ;; - MEDIUM) - if [ "$critical_count" -gt 0 ] || [ "$high_count" -gt 0 ] || [ "$medium_count" -gt 0 ]; then - echo "❌ Medium, high, or critical vulnerabilities found - blocking deployment" - echo "status=failed" >> $GITHUB_OUTPUT - exit 1 - fi - ;; - LOW) - echo "ℹ️ All vulnerabilities reported for awareness" - ;; - esac - - echo "status=passed" >> $GITHUB_OUTPUT - echo "✅ Vulnerability scan passed security threshold" - - # Create summary report - report=$(jq -c '{ - critical: [.runs[].results[] | select(.level == "error")] | length, - high: [.runs[].results[] | select(.level == "warning")] | length, - medium: [.runs[].results[] | select(.level == "note")] | length, - threshold: "${{ inputs.security-threshold }}", - status: "passed" - }' trivy-results.sarif) - - echo "report=$report" >> $GITHUB_OUTPUT - - - name: Upload Trivy scan results - uses: actions/upload-artifact@v4 - with: - name: trivy-results - path: trivy-results.sarif - retention-days: 30 - - - name: Upload to GitHub Security tab - if: always() - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: trivy-results.sarif - # Registry cleanup cleanup: name: 🧹 Registry Cleanup runs-on: ubuntu-latest - needs: [prepare, build, scan] - if: inputs.cleanup-old-images == true && (needs.scan.result == 'success' || needs.scan.result == 'skipped') && needs.build.result == 'success' + needs: [prepare, build] + if: inputs.cleanup-old-images == true && needs.build.result == 'success' steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 @@ -584,7 +448,7 @@ jobs: summary: name: 📋 Deployment Summary runs-on: ubuntu-latest - needs: [prepare, build, scan, cleanup] + needs: [prepare, build, cleanup] if: always() && needs.build.result == 'success' steps: - name: Display deployment summary @@ -598,17 +462,6 @@ jobs: echo "Tags: ${{ needs.prepare.outputs.image-tags }}" echo "" - if [ "${{ inputs.vulnerability-scan }}" = "true" ]; then - if [ "${{ needs.scan.result }}" = "success" ]; then - echo "🔒 Security scan: ✅ PASSED" - echo "Security threshold: ${{ inputs.security-threshold }}" - else - echo "🔒 Security scan: ❌ FAILED" - fi - else - echo "🔒 Security scan: ⏭️ SKIPPED" - fi - if [ "${{ inputs.cleanup-old-images }}" = "true" ]; then if [ "${{ needs.cleanup.result }}" = "success" ]; then echo "🧹 Registry cleanup: ✅ COMPLETED" diff --git a/README.md b/README.md index 7a2a31a..b3b9040 100644 --- a/README.md +++ b/README.md @@ -107,18 +107,16 @@ jobs: ### Docker ECR Deployment -A comprehensive Docker container deployment workflow supporting multi-platform builds, ECR registry management, vulnerability scanning, and container security with build optimization and registry lifecycle management. +A comprehensive Docker container deployment workflow supporting multi-platform builds, ECR registry management, and container security with build optimization and registry lifecycle management. #### **Features** - **Multi-platform builds**: Support for linux/amd64, linux/arm64, and ARM variants - **ECR integration**: Automated ECR repository creation and lifecycle management -- **Vulnerability scanning**: Trivy security scanning with configurable thresholds - **Container signing**: Optional cosign-based image signing and attestation - **Smart tagging**: Multiple tagging strategies (latest, semantic, branch, custom) - **Build optimization**: Advanced caching with registry and inline cache support - **Registry cleanup**: Automated cleanup of old images with retention policies - **Multi-stage builds**: Support for target build stages and build arguments -- **Security gates**: Configurable vulnerability thresholds blocking insecure deployments #### **Inputs** | Name | Required | Type | Default | Description | @@ -131,9 +129,6 @@ A comprehensive Docker container deployment workflow supporting multi-platform b | **Platform and Build Configuration** | | platforms | ❌ | string | linux/amd64,linux/arm64 | Target platforms for multi-platform builds | | push-to-registry | ❌ | boolean | true | Push built images to ECR registry | -| **Security and Scanning** | -| vulnerability-scan | ❌ | boolean | true | Enable container vulnerability scanning | -| security-threshold | ❌ | string | HIGH | Security vulnerability threshold (CRITICAL/HIGH/MEDIUM/LOW) | | **Tagging Strategy** | | tag-strategy | ❌ | string | latest | Image tagging strategy (latest/semantic/branch/custom) | | custom-tags | ❌ | string | | Custom tags (comma-separated) when using custom strategy | @@ -162,7 +157,6 @@ A comprehensive Docker container deployment workflow supporting multi-platform b | image-uri | Full URI of the built container image | | image-digest | SHA256 digest of the built image | | image-tags | Applied image tags as JSON array | -| vulnerability-report | Container vulnerability scan results | #### **Example Usage** @@ -179,16 +173,14 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} ``` -**Multi-platform with Vulnerability Scanning:** +**Multi-platform with Semantic Tagging:** ```yaml jobs: - secure-deploy: + multi-platform-deploy: uses: aligent/workflows/.github/workflows/docker-ecr-deploy.yml@main with: - ecr-repository: my-secure-app + ecr-repository: my-app platforms: "linux/amd64,linux/arm64" - vulnerability-scan: true - security-threshold: "CRITICAL" tag-strategy: "semantic" secrets: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -206,7 +198,6 @@ jobs: enable-signing: true cleanup-old-images: true retention-count: "5" - security-threshold: "HIGH" aws-region: "us-east-1" secrets: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} From 301dad74f1a69990d5ea4b4b22ad47aa3ba580e0 Mon Sep 17 00:00:00 2001 From: Daniel van der Ploeg Date: Mon, 15 Sep 2025 15:24:56 +0930 Subject: [PATCH 4/6] feat: remove extra bloat that claude added --- .github/workflows/docker-ecr-deploy.yml | 129 ++---------------------- README.md | 12 +-- 2 files changed, 11 insertions(+), 130 deletions(-) diff --git a/.github/workflows/docker-ecr-deploy.yml b/.github/workflows/docker-ecr-deploy.yml index f682d05..4d140d6 100644 --- a/.github/workflows/docker-ecr-deploy.yml +++ b/.github/workflows/docker-ecr-deploy.yml @@ -65,18 +65,6 @@ on: required: false default: "" - # Registry Management - cleanup-old-images: - description: "Clean up old images from ECR registry" - type: boolean - required: false - default: false - retention-count: - description: "Number of images to retain when cleaning up" - type: string - required: false - default: "10" - # Container Signing enable-signing: description: "Enable container image signing with cosign" @@ -309,41 +297,17 @@ jobs: with: mask-password: 'true' - - name: Create ECR repository if needed + - name: Verify ECR repository exists run: | - echo "🗄️ Ensuring ECR repository exists..." + echo "🔍 Verifying ECR repository exists..." - aws ecr describe-repositories --repository-names ${{ inputs.ecr-repository }} --region ${{ inputs.aws-region }} 2>/dev/null || { - echo "📦 Creating ECR repository: ${{ inputs.ecr-repository }}" - aws ecr create-repository \ - --repository-name ${{ inputs.ecr-repository }} \ - --region ${{ inputs.aws-region }} \ - --image-scanning-configuration scanOnPush=true \ - --encryption-configuration encryptionType=AES256 - - # Set lifecycle policy - aws ecr put-lifecycle-policy \ - --repository-name ${{ inputs.ecr-repository }} \ - --region ${{ inputs.aws-region }} \ - --lifecycle-policy-text '{ - "rules": [ - { - "rulePriority": 1, - "description": "Keep last 30 images", - "selection": { - "tagStatus": "any", - "countType": "imageCountMoreThan", - "countNumber": 30 - }, - "action": { - "type": "expire" - } - } - ] - }' - } + if ! aws ecr describe-repositories --repository-names ${{ inputs.ecr-repository }} --region ${{ inputs.aws-region }} 2>/dev/null; then + echo "❌ Error: ECR repository '${{ inputs.ecr-repository }}' does not exist in region ${{ inputs.aws-region }}" + echo "Please create the ECR repository before running this workflow" + exit 1 + fi - echo "✅ ECR repository ready" + echo "✅ ECR repository verified" - name: Build and push Docker image id: build @@ -403,80 +367,3 @@ jobs: rm cosign.key echo "✅ Image signed successfully" - # Registry cleanup - cleanup: - name: 🧹 Registry Cleanup - runs-on: ubuntu-latest - needs: [prepare, build] - if: inputs.cleanup-old-images == true && needs.build.result == 'success' - steps: - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - aws-access-key-id: ${{ secrets.aws-access-key-id }} - aws-secret-access-key: ${{ secrets.aws-secret-access-key }} - aws-region: ${{ inputs.aws-region }} - - - name: Clean up old images - run: | - echo "🧹 Cleaning up old images from ECR..." - - # Get list of images sorted by pushed date - images=$(aws ecr describe-images \ - --repository-name ${{ inputs.ecr-repository }} \ - --region ${{ inputs.aws-region }} \ - --query 'sort_by(imageDetails, &imagePushedAt)[:-${{ inputs.retention-count }}].imageDigest' \ - --output text) - - if [ -n "$images" ] && [ "$images" != "None" ]; then - echo "🗑️ Found $(echo $images | wc -w) images to delete" - - for digest in $images; do - echo "Deleting image: $digest" - aws ecr batch-delete-image \ - --repository-name ${{ inputs.ecr-repository }} \ - --region ${{ inputs.aws-region }} \ - --image-ids imageDigest=$digest - done - - echo "✅ Cleanup completed" - else - echo "ℹ️ No images to clean up" - fi - - # Deployment summary - summary: - name: 📋 Deployment Summary - runs-on: ubuntu-latest - needs: [prepare, build, cleanup] - if: always() && needs.build.result == 'success' - steps: - - name: Display deployment summary - run: | - echo "📋 Docker ECR Deployment Summary" - echo "==================================" - echo "Repository: ${{ inputs.ecr-repository }}" - echo "Region: ${{ inputs.aws-region }}" - echo "Platforms: ${{ needs.prepare.outputs.platforms }}" - echo "Image URI: ${{ needs.build.outputs.image-uri }}" - echo "Tags: ${{ needs.prepare.outputs.image-tags }}" - echo "" - - if [ "${{ inputs.cleanup-old-images }}" = "true" ]; then - if [ "${{ needs.cleanup.result }}" = "success" ]; then - echo "🧹 Registry cleanup: ✅ COMPLETED" - else - echo "🧹 Registry cleanup: ❌ FAILED" - fi - else - echo "🧹 Registry cleanup: ⏭️ SKIPPED" - fi - - if [ "${{ inputs.enable-signing }}" = "true" ]; then - echo "✍️ Image signing: ✅ ENABLED" - else - echo "✍️ Image signing: ⏭️ DISABLED" - fi - - echo "" - echo "🎉 Docker deployment completed successfully!" \ No newline at end of file diff --git a/README.md b/README.md index b3b9040..be26956 100644 --- a/README.md +++ b/README.md @@ -107,15 +107,14 @@ jobs: ### Docker ECR Deployment -A comprehensive Docker container deployment workflow supporting multi-platform builds, ECR registry management, and container security with build optimization and registry lifecycle management. +A comprehensive Docker container deployment workflow supporting multi-platform builds, ECR registry management, and container security with build optimization and registry lifecycle management. Note: The ECR repository must exist before running this workflow. #### **Features** - **Multi-platform builds**: Support for linux/amd64, linux/arm64, and ARM variants -- **ECR integration**: Automated ECR repository creation and lifecycle management +- **ECR integration**: Push images to existing ECR repositories - **Container signing**: Optional cosign-based image signing and attestation - **Smart tagging**: Multiple tagging strategies (latest, semantic, branch, custom) - **Build optimization**: Advanced caching with registry and inline cache support -- **Registry cleanup**: Automated cleanup of old images with retention policies - **Multi-stage builds**: Support for target build stages and build arguments #### **Inputs** @@ -136,9 +135,6 @@ A comprehensive Docker container deployment workflow supporting multi-platform b | cache-from | ❌ | string | | Cache sources for build optimization (comma-separated) | | build-args | ❌ | string | {} | Docker build arguments as JSON object | | target-stage | ❌ | string | | Target build stage for multi-stage Dockerfiles | -| **Registry Management** | -| cleanup-old-images | ❌ | boolean | false | Clean up old images from ECR registry | -| retention-count | ❌ | string | 10 | Number of images to retain when cleaning up | | **Container Signing** | | enable-signing | ❌ | boolean | false | Enable container image signing with cosign | | **Advanced Configuration** | @@ -187,7 +183,7 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} ``` -**Production with Signing and Cleanup:** +**Production with Signing:** ```yaml jobs: production-deploy: @@ -196,8 +192,6 @@ jobs: ecr-repository: my-prod-app tag-strategy: "semantic" enable-signing: true - cleanup-old-images: true - retention-count: "5" aws-region: "us-east-1" secrets: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} From adc2ec96b7d3d543f598a1c5a04b46b67562f689 Mon Sep 17 00:00:00 2001 From: Daniel van der Ploeg Date: Mon, 15 Sep 2025 15:28:51 +0930 Subject: [PATCH 5/6] feat: verify ecr repository exists in prepare job --- .github/workflows/docker-ecr-deploy.yml | 30 ++++++++++++++----------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/.github/workflows/docker-ecr-deploy.yml b/.github/workflows/docker-ecr-deploy.yml index 4d140d6..4fb454f 100644 --- a/.github/workflows/docker-ecr-deploy.yml +++ b/.github/workflows/docker-ecr-deploy.yml @@ -112,7 +112,14 @@ jobs: cache-config: ${{ steps.cache-config.outputs.setup }} platforms: ${{ steps.platform-config.outputs.platforms }} steps: - - name: Validate required inputs + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.aws-access-key-id }} + aws-secret-access-key: ${{ secrets.aws-secret-access-key }} + aws-region: ${{ inputs.aws-region }} + + - name: Validate required inputs and ECR repository run: | echo "🔍 Validating Docker build configuration..." @@ -121,6 +128,15 @@ jobs: exit 1 fi + # Verify ECR repository exists + echo "🔍 Verifying ECR repository exists..." + if ! aws ecr describe-repositories --repository-names ${{ inputs.ecr-repository }} --region ${{ inputs.aws-region }} 2>/dev/null; then + echo "❌ Error: ECR repository '${{ inputs.ecr-repository }}' does not exist in region ${{ inputs.aws-region }}" + echo "Please create the ECR repository before running this workflow" + exit 1 + fi + echo "✅ ECR repository verified" + # Validate tag strategy case "${{ inputs.tag-strategy }}" in latest|semantic|branch|custom) @@ -297,18 +313,6 @@ jobs: with: mask-password: 'true' - - name: Verify ECR repository exists - run: | - echo "🔍 Verifying ECR repository exists..." - - if ! aws ecr describe-repositories --repository-names ${{ inputs.ecr-repository }} --region ${{ inputs.aws-region }} 2>/dev/null; then - echo "❌ Error: ECR repository '${{ inputs.ecr-repository }}' does not exist in region ${{ inputs.aws-region }}" - echo "Please create the ECR repository before running this workflow" - exit 1 - fi - - echo "✅ ECR repository verified" - - name: Build and push Docker image id: build uses: docker/build-push-action@v5 From e0ad3487b96490b9c72aa9f3b8d04a210e057a9d Mon Sep 17 00:00:00 2001 From: Daniel van der Ploeg Date: Mon, 15 Sep 2025 16:04:14 +0930 Subject: [PATCH 6/6] chore: update readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index be26956..caa48c1 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,9 @@ jobs: ### Docker ECR Deployment -A comprehensive Docker container deployment workflow supporting multi-platform builds, ECR registry management, and container security with build optimization and registry lifecycle management. Note: The ECR repository must exist before running this workflow. +A comprehensive Docker container deployment workflow supporting multi-platform builds, ECR registry management, and container signing with build optimization. + +**Important:** The ECR repository must exist before running this workflow - the workflow will fail if the repository doesn't exist. #### **Features** - **Multi-platform builds**: Support for linux/amd64, linux/arm64, and ARM variants