name: Shared - Docker Artifact to ECR
on: workflow_call: inputs: runner: description: 'Runner type' required: false type: string default: 'ubuntu-latest' image_tag: description: 'Docker image tag. If empty, uses short commit SHA (7 chars)' required: false type: string default: '' push_latest: description: 'Also push :latest tag' required: false type: boolean default: false dockerfile_path: description: 'Path to Dockerfile (build context)' required: false type: string default: '.' docker_platform: description: 'Docker platform (linux/amd64, linux/arm64)' required: false type: string default: 'linux/arm64' environment: description: 'GitHub environment (develop, prod)' required: false type: string default: 'develop'
# Optional: Java/Gradle build before Docker (skip for non-Java stacks) build_from_source: description: 'Build JAR from source before Docker build (Java projects)' required: false type: boolean default: false java_version: description: 'Java version (only when build_from_source: true)' required: false type: string default: '21' java_distribution: description: 'Java distribution (only when build_from_source: true)' required: false type: string default: 'temurin' build_tool: description: 'Build tool: gradle or maven (only when build_from_source: true)' required: false type: string default: 'gradle'
# Optional: Download pre-built artifact artifact_name: description: 'Name of pre-built artifact to download. If empty, no download.' required: false type: string default: '' artifact_path: description: 'Path where artifact is downloaded' required: false type: string default: 'bootstrap/build/libs'
secrets: AWS_ACCESS_KEY_ID: required: true AWS_SECRET_ACCESS_KEY: required: true AWS_REGION: required: true
outputs: image_tag: description: 'Docker image tag pushed' value: ${{ jobs.artifact.outputs.image_tag }} image_uri: description: 'Full Docker image URI' value: ${{ jobs.artifact.outputs.image_uri }} commit_sha: description: 'Short commit SHA used' value: ${{ jobs.artifact.outputs.commit_sha }}
jobs: artifact: name: Build and Push to ECR runs-on: ${{ inputs.runner }} environment: ${{ inputs.environment }} timeout-minutes: 20
outputs: image_tag: ${{ steps.set-tag.outputs.tag }} image_uri: ${{ steps.push-ecr.outputs.image_uri }} commit_sha: ${{ steps.set-tag.outputs.commit_sha }}
steps: - name: Checkout uses: actions/checkout@v5
- name: Determine image tag id: set-tag run: | COMMIT_SHA="${{ github.sha }}" SHORT_SHA="${COMMIT_SHA:0:7}"
if [ -n "${{ inputs.image_tag }}" ]; then TAG="${{ inputs.image_tag }}" else TAG="$SHORT_SHA" fi
echo "tag=$TAG" >> $GITHUB_OUTPUT echo "commit_sha=$SHORT_SHA" >> $GITHUB_OUTPUT echo "Using image tag: $TAG"
# ============================================ # OPTIONAL: Download pre-built artifact # ============================================ - name: Download build artifact if: inputs.artifact_name != '' uses: actions/download-artifact@v5 with: name: ${{ inputs.artifact_name }} path: ${{ inputs.artifact_path }}
# ============================================ # OPTIONAL: Build from source (Java/Gradle/Maven) # ============================================ - name: Setup JDK ${{ inputs.java_version }} if: inputs.build_from_source && inputs.artifact_name == '' uses: actions/setup-java@v5 with: distribution: ${{ inputs.java_distribution }} java-version: ${{ inputs.java_version }}
- name: Setup Gradle if: inputs.build_from_source && inputs.artifact_name == '' && inputs.build_tool == 'gradle' uses: gradle/actions/setup-gradle@v4 with: cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/master' }}
- name: Build JAR (Gradle) if: inputs.build_from_source && inputs.artifact_name == '' && inputs.build_tool == 'gradle' env: GH_PACKAGES_USERNAME: ${{ secrets.GH_PACKAGES_USERNAME }} GH_PACKAGES_TOKEN: ${{ secrets.GH_PACKAGES_TOKEN }} run: ./gradlew clean build -x test --no-daemon --build-cache --parallel
- name: Build JAR (Maven) if: inputs.build_from_source && inputs.artifact_name == '' && inputs.build_tool == 'maven' env: GH_PACKAGES_USERNAME: ${{ secrets.GH_PACKAGES_USERNAME }} GH_PACKAGES_TOKEN: ${{ secrets.GH_PACKAGES_TOKEN }} run: mvn clean package -DskipTests -B
# ============================================ # AWS + DOCKER BUILD & PUSH # ============================================ - 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: ${{ secrets.AWS_REGION }}
- name: Ensure ECR repository exists env: ECR_REPOSITORY: ${{ github.event.repository.name }} AWS_REGION: ${{ secrets.AWS_REGION }} run: | if aws ecr describe-repositories \ --repository-names "$ECR_REPOSITORY" \ --region "$AWS_REGION" >/dev/null 2>&1; then echo "ECR repository '$ECR_REPOSITORY' already exists." else echo "Creating ECR repository '$ECR_REPOSITORY' in region '$AWS_REGION'..." aws ecr create-repository \ --repository-name "$ECR_REPOSITORY" \ --region "$AWS_REGION" \ --image-tag-mutability MUTABLE \ --image-scanning-configuration scanOnPush=true \ --tags Key=managed-by,Value=ci-templates Key=repository,Value=$ECR_REPOSITORY echo "ECR repository '$ECR_REPOSITORY' created." fi
- name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v2 with: mask-aws-account-id: 'false'
- name: Set up QEMU uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx uses: docker/setup-buildx-action@v3
- name: Build and push to ECR id: push-ecr env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} ECR_REPOSITORY: ${{ github.event.repository.name }} IMAGE_TAG: ${{ steps.set-tag.outputs.tag }} run: | IMAGE_URI="$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
TAGS="-t $IMAGE_URI" if [ "${{ inputs.push_latest }}" = "true" ]; then TAGS="$TAGS -t $ECR_REGISTRY/$ECR_REPOSITORY:latest" fi
echo "Building and pushing Docker image: $IMAGE_URI" echo "Platform: ${{ inputs.docker_platform }}" docker buildx build \ --platform ${{ inputs.docker_platform }} \ --no-cache \ $TAGS \ --label "org.opencontainers.image.source=https://github.com/${{ github.repository }}" \ --label "org.opencontainers.image.revision=${{ github.sha }}" \ --label "org.opencontainers.image.created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \ --label "org.opencontainers.image.title=$ECR_REPOSITORY" \ --label "org.opencontainers.image.version=$IMAGE_TAG" \ --label "org.opencontainers.image.vendor=codehunters" \ --label "build.github.run-id=${{ github.run_id }}" \ --label "build.github.workflow=${{ github.workflow }}" \ --label "build.environment=${{ inputs.environment }}" \ --label "observability.service=$ECR_REPOSITORY" \ --label "observability.environment=${{ inputs.environment }}" \ --label "observability.domain=${ECR_REPOSITORY%%-*}" \ --push \ ${{ inputs.dockerfile_path }}
echo "image_uri=$IMAGE_URI" >> $GITHUB_OUTPUT
echo "### Docker Image Pushed to ECR" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY echo "| **Registry** | $ECR_REGISTRY |" >> $GITHUB_STEP_SUMMARY echo "| **Repository** | $ECR_REPOSITORY |" >> $GITHUB_STEP_SUMMARY echo "| **Tag** | $IMAGE_TAG |" >> $GITHUB_STEP_SUMMARY echo "| **Platform** | ${{ inputs.docker_platform }} |" >> $GITHUB_STEP_SUMMARY echo "| **Full URI** | $IMAGE_URI |" >> $GITHUB_STEP_SUMMARY Shared (cross-cutting)· Reusable workflow ·on: workflow_call
Shared Artifact Docker Ecr
Shared - Docker Artifact to ECR
.github/workflows/shared-artifact-docker-ecr.yml