- Check if kubectl already exists first - Use specific version instead of querying stable.txt - Show download progress - Skip installation if already present This should significantly reduce setup time if kubectl is already included in the runner image
290 lines
10 KiB
YAML
290 lines
10 KiB
YAML
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
|
||
echo "✅ kubectl already installed"
|
||
kubectl version --client
|
||
else
|
||
echo "📥 Installing kubectl..."
|
||
# Use specific version to avoid querying stable.txt
|
||
KUBECTL_VERSION="v1.31.0"
|
||
curl -LO "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/arm64/kubectl" &
|
||
DOWNLOAD_PID=$!
|
||
|
||
# Show progress
|
||
while kill -0 $DOWNLOAD_PID 2>/dev/null; do
|
||
echo -n "."
|
||
sleep 1
|
||
done
|
||
wait $DOWNLOAD_PID
|
||
|
||
echo ""
|
||
chmod +x kubectl
|
||
sudo mv kubectl /usr/local/bin/
|
||
kubectl version --client
|
||
fi
|
||
|
||
- 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: <branch>-sha-<full-40-char-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"
|