name: Build Docker Image on: push: branches: [main, develop] tags: - 'v*' workflow_dispatch: env: REGISTRY: gitea0213.kro.kr IMAGE_NAME: ${{ github.repository }} jobs: build-and-push: runs-on: ubuntu-24.04-arm permissions: contents: write packages: write outputs: image-tag: ${{ steps.meta.outputs.tags }} image-digest: ${{ steps.build.outputs.digest }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup kubectl run: | if ! command -v kubectl &> /dev/null; then curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl" chmod +x kubectl sudo mv kubectl /usr/local/bin/ fi kubectl version --client - name: Setup kubeconfig from Secret env: KUBECONFIG_B64: ${{ secrets.KUBECONFIG }} run: | if [ -z "$KUBECONFIG_B64" ]; then echo "❌ KUBECONFIG secret not set" echo "Please add KUBECONFIG to Gitea repository secrets" exit 1 fi echo "📝 KUBECONFIG secret found (length: ${#KUBECONFIG_B64})" mkdir -p $HOME/.kube # Clean up the base64 string (remove all whitespace and newlines) CLEAN_B64=$(echo "$KUBECONFIG_B64" | tr -d '[:space:]') echo "After cleanup: ${#CLEAN_B64} chars" # Try decoding if echo "$CLEAN_B64" | base64 -d > $HOME/.kube/config 2>/dev/null; then echo "✅ Successfully decoded kubeconfig" elif echo "$CLEAN_B64" | base64 --decode > $HOME/.kube/config 2>/dev/null; then echo "✅ Successfully decoded kubeconfig (with --decode)" else echo "❌ Both base64 decode methods failed" echo "Trying to save as-is (maybe already decoded)..." echo "$KUBECONFIG_B64" > $HOME/.kube/config fi chmod 600 $HOME/.kube/config # Verify it's valid YAML if head -1 $HOME/.kube/config | grep -q "apiVersion"; then echo "✅ Kubeconfig appears valid" else echo "âš ī¸ Kubeconfig may not be valid, first line:" head -1 $HOME/.kube/config fi # Test connection kubectl cluster-info kubectl get nodes -o wide - name: Lowercase repository name id: lowercase run: | echo "repo=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT - name: Extract metadata (tags, labels) id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ steps.lowercase.outputs.repo }} tags: | type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha,prefix={{branch}}-sha-,format=long type=raw,value=latest,enable={{is_default_branch}} - name: Create registry credentials in Kubernetes run: | # Ensure namespace exists kubectl get namespace kaniko-builds 2>/dev/null || kubectl create namespace kaniko-builds # Create/update registry secret kubectl create secret docker-registry kaniko-registry-creds \ --docker-server=${{ env.REGISTRY }} \ --docker-username=bluemayne \ --docker-password=${{ secrets.GITEAREGISTRY }} \ --namespace=kaniko-builds \ --dry-run=client -o yaml | kubectl apply -f - - name: Build and push with Kaniko Job id: build run: | TAGS="${{ steps.meta.outputs.tags }}" # Prepare destination arguments DESTINATIONS="" while IFS= read -r tag; do DESTINATIONS="$DESTINATIONS\n - --destination=$tag" done <<< "$TAGS" # Create unique build name BUILD_NAME="kaniko-build-${{ github.run_number }}-$(date +%s)" echo "đŸ“Ļ Building image: ${BUILD_NAME}" echo "Tags: $TAGS" # Generate Kaniko Job from template sed -e "s|KANIKO_BUILD_NAME|${BUILD_NAME}|g" \ -e "s|GIT_REPO_URL|https://gitea0213.kro.kr/${{ github.repository }}.git|g" \ -e "s|GIT_SHA|${{ github.sha }}|g" \ -e "s|CACHE_REPO|${{ env.REGISTRY }}/${{ steps.lowercase.outputs.repo }}/cache|g" \ -e "s|# DESTINATIONS will be added here|${DESTINATIONS}|g" \ deploy/kaniko/job.yaml > /tmp/kaniko-job.yaml # Apply Job kubectl apply -f /tmp/kaniko-job.yaml # Wait for completion echo "âŗ Waiting for Kaniko Job to complete..." kubectl wait --for=condition=complete --timeout=600s job/${BUILD_NAME} -n kaniko-builds || { echo "❌ Job failed or timed out" POD=$(kubectl get pods -n kaniko-builds -l job-name=${BUILD_NAME} -o jsonpath='{.items[0].metadata.name}') echo "📋 Logs:" kubectl logs -n kaniko-builds ${POD} --all-containers=true || true kubectl delete job ${BUILD_NAME} -n kaniko-builds || true exit 1 } echo "✅ Image built successfully" # Get digest POD=$(kubectl get pods -n kaniko-builds -l job-name=${BUILD_NAME} -o jsonpath='{.items[0].metadata.name}') DIGEST=$(kubectl logs -n kaniko-builds ${POD} -c kaniko 2>/dev/null | grep -oP 'digest: \K[a-zA-Z0-9:]+' | tail -1 || echo "unknown") echo "digest=${DIGEST}" >> $GITHUB_OUTPUT # Cleanup kubectl delete job ${BUILD_NAME} -n kaniko-builds || true - name: Extract SHA tag id: extract-tag run: | # Extract the SHA-based tag from the tags list TAGS="${{ steps.meta.outputs.tags }}" echo "All tags:" echo "$TAGS" echo "---" # Get commit SHA (full 40 characters) COMMIT_SHA="${{ github.sha }}" # Get current branch name BRANCH_NAME="${{ github.ref_name }}" echo "Branch: $BRANCH_NAME" # Method 1: Extract the full SHA tag from docker/metadata-action output # docker/metadata-action creates: -sha- SHA_TAG=$(echo "$TAGS" | grep -oE "${BRANCH_NAME}-sha-[a-f0-9]{40}" | head -n 1) # Method 2: If not found, try to extract any branch-sha- tag (fallback) if [ -z "$SHA_TAG" ]; then SHA_TAG=$(echo "$TAGS" | grep -oE "${BRANCH_NAME}-sha-[a-f0-9]+" | head -n 1) if [ -n "$SHA_TAG" ]; then echo "âš ī¸ Found SHA tag (may not be full 40 chars): $SHA_TAG" fi fi # Method 3: Fallback to commit SHA directly (construct the tag) if [ -z "$SHA_TAG" ]; then SHA_TAG="${BRANCH_NAME}-sha-$COMMIT_SHA" echo "âš ī¸ Could not extract from tags, using commit SHA: $SHA_TAG" fi if [ -z "$SHA_TAG" ]; then echo "❌ ERROR: Failed to extract SHA tag" exit 1 fi echo "sha-tag=$SHA_TAG" >> $GITHUB_OUTPUT echo "✅ Extracted SHA tag: $SHA_TAG" - name: Update kustomization with new image tag env: GITEA_TOKEN: ${{ secrets.GITEAREGISTRYTOKEN }} run: | git config --global user.name "gitea-actions[bot]" git config --global user.email "gitea-actions[bot]@users.noreply.gitea.com" # Validate that SHA_TAG is not empty SHA_TAG="${{ steps.extract-tag.outputs.sha-tag }}" if [ -z "$SHA_TAG" ]; then echo "❌ ERROR: SHA_TAG is empty, cannot update kustomization" exit 1 fi # Determine overlay based on branch BRANCH_NAME="${{ github.ref_name }}" if [ "$BRANCH_NAME" = "main" ]; then OVERLAY="prod" elif [ "$BRANCH_NAME" = "develop" ]; then OVERLAY="dev" else echo "âš ī¸ Unknown branch: $BRANCH_NAME, skipping kustomization update" exit 0 fi KUSTOMIZATION_FILE="deploy/k8s/overlays/$OVERLAY/kustomization.yaml" # Check if kustomization file has images section if grep -q "images:" "$KUSTOMIZATION_FILE"; then echo "📝 Updating $KUSTOMIZATION_FILE with tag: $SHA_TAG" # Update kustomization.yaml with new image tag # Handle both cases: newTag: (with value) and newTag: (empty) sed -i.bak "s|newTag:.*|newTag: $SHA_TAG|" "$KUSTOMIZATION_FILE" # Verify the update was successful if grep -q "newTag: $SHA_TAG" "$KUSTOMIZATION_FILE"; then echo "✅ Successfully updated kustomization.yaml" rm -f "$KUSTOMIZATION_FILE.bak" else echo "❌ ERROR: Failed to update kustomization.yaml" cat "$KUSTOMIZATION_FILE" exit 1 fi # Commit and push if there are changes if git diff --quiet; then echo "No changes to commit" else git add "$KUSTOMIZATION_FILE" git commit -m "Update $OVERLAY image to $SHA_TAG" git push echo "✅ Kustomization updated with new image tag: $SHA_TAG" fi else echo "â„šī¸ $OVERLAY overlay uses base image (latest tag), skipping kustomization update" echo " Image built with tag: $SHA_TAG" fi - name: Display image information run: | echo "✅ Image built and pushed successfully!" echo "đŸ“Ļ Image tags:" echo "${{ steps.meta.outputs.tags }}" echo "🔖 SHA tag: ${{ steps.extract-tag.outputs.sha-tag }}" echo "🔖 Digest: ${{ steps.build.outputs.digest }}" echo "" echo "🚀 Kustomization updated with new image tag" echo " ArgoCD will automatically detect and deploy this new image" echo " Monitor deployment at your ArgoCD dashboard"