diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index 796e06e..4c30ae4 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -4,6 +4,9 @@ on: push: branches: ['main'] paths: ['be-flask/**'] + pull_request: + paths: ['be-flask/**'] + types: ['opened', 'synchronize', 'reopened', 'labeled'] workflow_dispatch: inputs: environment: @@ -60,41 +63,73 @@ jobs: git tag "v$NEW_VERSION" git push origin main --tags + pr-check: + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + outputs: + should_deploy: ${{ steps.check.outputs.should_deploy }} + pr_number: ${{ steps.check.outputs.pr_number }} + steps: + - name: Check if PR should be deployed + id: check + run: | + PR_NUMBER="${{ github.event.pull_request.number }}" + echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + + # Check if PR has 'deploy' label + if echo '${{ toJson(github.event.pull_request.labels.*.name) }}' | grep -q '"deploy"'; then + echo "should_deploy=true" >> $GITHUB_OUTPUT + echo "PR #$PR_NUMBER has deploy label - will create preview environment" + else + echo "should_deploy=false" >> $GITHUB_OUTPUT + echo "PR #$PR_NUMBER does not have deploy label - skipping deployment" + fi + build-and-push: - needs: version-bump - if: always() && (needs.version-bump.result == 'success' || github.event_name == 'workflow_dispatch') + needs: [version-bump, pr-check] + if: always() && (needs.version-bump.result == 'success' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && needs.pr-check.outputs.should_deploy == 'true')) runs-on: ubuntu-latest outputs: version: ${{ needs.version-bump.outputs.new_version || github.event.inputs.version || 'manual' }} + image_tag: ${{ steps.set_version.outputs.image_tag }} steps: - name: Checkout uses: actions/checkout@v4 - - - name: Set version for manual runs + + - name: Set version and tags id: set_version run: | - if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + if [ "${{ github.event_name }}" == "pull_request" ]; then + # For PRs, use PR-specific tag + PR_NUMBER="${{ needs.pr-check.outputs.pr_number }}" + IMAGE_TAG="pr-$PR_NUMBER" + echo "build_version=pr-$PR_NUMBER" >> $GITHUB_OUTPUT + echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT + elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then if [ -f be-flask/version.txt ]; then VERSION=$(cat be-flask/version.txt) else VERSION="manual-$(date +%Y%m%d-%H%M%S)" fi echo "build_version=$VERSION" >> $GITHUB_OUTPUT + echo "image_tag=v$VERSION" >> $GITHUB_OUTPUT else echo "build_version=${{ needs.version-bump.outputs.new_version }}" >> $GITHUB_OUTPUT + echo "image_tag=v${{ needs.version-bump.outputs.new_version }}" >> $GITHUB_OUTPUT fi echo "short_sha=${GITHUB_SHA:0:7}" >> $GITHUB_OUTPUT - + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - + - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_TOKEN }} - - - name: Build and push + + - name: Build and push for main branch + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' uses: docker/build-push-action@v5 with: context: be-flask @@ -102,12 +137,23 @@ jobs: push: true tags: | projectred521/flask-w:latest - projectred521/flask-w:v${{ steps.set_version.outputs.build_version }} + projectred521/flask-w:${{ steps.set_version.outputs.image_tag }} + projectred521/flask-w:${{ steps.set_version.outputs.short_sha }} + + - name: Build and push for PR + if: github.event_name == 'pull_request' + uses: docker/build-push-action@v5 + with: + context: be-flask + platforms: linux/amd64,linux/arm64 + push: true + tags: | + projectred521/flask-w:${{ steps.set_version.outputs.image_tag }} projectred521/flask-w:${{ steps.set_version.outputs.short_sha }} update-gitops: needs: [version-bump, build-and-push] - if: always() && needs.build-and-push.result == 'success' + if: always() && needs.build-and-push.result == 'success' && github.event_name != 'pull_request' runs-on: ubuntu-latest steps: - name: Checkout main repository @@ -150,7 +196,7 @@ jobs: git push origin main notify: - needs: [version-bump, build-and-push, update-gitops] + needs: [version-bump, build-and-push, update-gitops, pr-check] if: always() runs-on: ubuntu-latest steps: @@ -160,45 +206,92 @@ jobs: VERSION_STATUS="${{ needs.version-bump.result }}" BUILD_STATUS="${{ needs.build-and-push.result }}" GITOPS_STATUS="${{ needs.update-gitops.result }}" - - # Determine version for display - if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + PR_CHECK_STATUS="${{ needs.pr-check.result }}" + + # Determine deployment type and details + if [ "${{ github.event_name }}" == "pull_request" ]; then + PR_NUMBER="${{ needs.pr-check.outputs.pr_number }}" + SHOULD_DEPLOY="${{ needs.pr-check.outputs.should_deploy }}" + + if [ "$SHOULD_DEPLOY" == "true" ]; then + DISPLAY_VERSION="pr-$PR_NUMBER" + ENVIRONMENT="PR Preview" + PREVIEW_URL="http://pr-$PR_NUMBER.vimex.local" + + if [[ "$BUILD_STATUS" == "success" ]]; then + BUILD_LINE="**Docker Build:** ✅ PR image built" + GITOPS_LINE="**Preview Environment:** 🔄 ArgoCD will deploy automatically\\n**Preview URL:** <$PREVIEW_URL|pr-$PR_NUMBER.vimex.local>" + HEADER="🔍 PR Preview Environment Ready for #$PR_NUMBER" + OVERALL_SUCCESS=true + else + BUILD_LINE="**Docker Build:** ❌ Failed" + GITOPS_LINE="**Preview Environment:** ❌ Not available" + HEADER="❌ PR Preview Environment Failed for #$PR_NUMBER" + OVERALL_SUCCESS=false + fi + VERSION_LINE="**PR:** #$PR_NUMBER" + else + HEADER="â„šī¸ PR #$PR_NUMBER - No deployment (missing 'deploy' label)" + VERSION_LINE="**PR:** #$PR_NUMBER (no deploy label)" + BUILD_LINE="**Docker Build:** â­ī¸ Skipped" + GITOPS_LINE="**Preview Environment:** â­ī¸ Skipped" + OVERALL_SUCCESS=true + fi + elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then DISPLAY_VERSION="manual deployment" ENVIRONMENT="${{ github.event.inputs.environment }}" + VERSION_LINE="**Version:** ✅ $DISPLAY_VERSION" + + if [[ "$BUILD_STATUS" == "success" ]]; then + BUILD_LINE="**Docker Build:** ✅ Completed" + else + BUILD_LINE="**Docker Build:** ❌ Failed" + fi + + if [[ "$GITOPS_STATUS" == "success" ]]; then + GITOPS_LINE="**GitOps Update:** ✅ $ENVIRONMENT environment updated" + HEADER="🚀 Manual deployment successful for ${{ github.repository }}" + OVERALL_SUCCESS=true + else + GITOPS_LINE="**GitOps Update:** ❌ Failed" + HEADER="❌ Manual deployment failed for ${{ github.repository }}" + OVERALL_SUCCESS=false + fi else + # Main branch push DISPLAY_VERSION="v${{ needs.version-bump.outputs.new_version }}" ENVIRONMENT="staging" + + if [[ "$VERSION_STATUS" == "success" ]]; then + VERSION_LINE="**Version:** ✅ $DISPLAY_VERSION" + else + VERSION_LINE="**Version:** ❌ Failed" + fi + + if [[ "$BUILD_STATUS" == "success" ]]; then + BUILD_LINE="**Docker Build:** ✅ Completed" + else + BUILD_LINE="**Docker Build:** ❌ Failed" + fi + + if [[ "$GITOPS_STATUS" == "success" ]]; then + GITOPS_LINE="**GitOps Update:** ✅ $ENVIRONMENT environment updated" + else + GITOPS_LINE="**GitOps Update:** ❌ Failed" + fi + + if [[ "$BUILD_STATUS" == "success" && "$GITOPS_STATUS" == "success" ]]; then + HEADER="🚀 Deployment successful for ${{ github.repository }} ($DISPLAY_VERSION)" + OVERALL_SUCCESS=true + else + HEADER="❌ Deployment failed for ${{ github.repository }}" + OVERALL_SUCCESS=false + fi fi - - # Build status lines - if [[ "$VERSION_STATUS" == "success" || "${{ github.event_name }}" == "workflow_dispatch" ]]; then - VERSION_LINE="**Version:** ✅ $DISPLAY_VERSION" - else - VERSION_LINE="**Version:** ❌ Failed" - fi - - if [[ "$BUILD_STATUS" == "success" ]]; then - BUILD_LINE="**Docker Build:** ✅ Completed" - else - BUILD_LINE="**Docker Build:** ❌ Failed" - fi - - if [[ "$GITOPS_STATUS" == "success" ]]; then - GITOPS_LINE="**GitOps Update:** ✅ $ENVIRONMENT environment updated" - else - GITOPS_LINE="**GitOps Update:** ❌ Failed" - fi - - # Overall status - if [[ "$BUILD_STATUS" == "success" && "$GITOPS_STATUS" == "success" ]]; then - HEADER="🚀 Deployment successful for ${{ github.repository }} ($DISPLAY_VERSION)" - else - HEADER="❌ Deployment failed for ${{ github.repository }}" - fi - + WORKFLOW_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" MESSAGE="$HEADER\\n$VERSION_LINE\\n$BUILD_LINE\\n$GITOPS_LINE\\n<$WORKFLOW_URL|View Deployment Details>" - + curl -X POST -H 'Content-type: application/json' \ --data "{\"text\":\"$MESSAGE\"}" \ ${{ secrets.SLACK_WEBHOOK_URL }} \ No newline at end of file diff --git a/README.md b/README.md index ab062cf..5b0487b 100644 --- a/README.md +++ b/README.md @@ -541,3 +541,4 @@ python test_keda_scaling.py --clear-queue image + diff --git a/argocd/github-token-secret.yaml b/argocd/github-token-secret.yaml new file mode 100644 index 0000000..d7cfa7e --- /dev/null +++ b/argocd/github-token-secret.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Secret +metadata: + name: github-token + namespace: argo +type: Opaque +data: + # For public repositories, you might not need a token, but if you do: + # Replace 'YOUR_GITHUB_TOKEN_HERE' with a base64-encoded GitHub Personal Access Token + # To create the token: echo -n 'your_actual_token' | base64 + # For public repos, you can often use an empty token: echo -n '' | base64 + token: "" # Empty for public repos, or base64-encoded token if needed \ No newline at end of file diff --git a/argocd/pr-envs-app-set.yaml b/argocd/pr-envs-app-set.yaml new file mode 100644 index 0000000..e6e211c --- /dev/null +++ b/argocd/pr-envs-app-set.yaml @@ -0,0 +1,91 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: pr-environments + namespace: argo + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + generators: + - pullRequest: + github: + owner: red512 + repo: vimex + labels: + - deploy + requeueAfterSeconds: 120 + template: + metadata: + name: 'pr-{{number}}-{{branch}}' + labels: + pr-number: '{{number}}' + branch: '{{branch}}' + environment: preview + finalizers: + - resources-finalizer.argocd.argoproj.io + spec: + project: default + source: + repoURL: https://github.com/red512/vimex-gitops + targetRevision: HEAD + path: gitops/helm/backend-helm-chart + helm: + valueFiles: + - values.yaml + parameters: + - name: image.tag + value: 'pr-{{number}}' + - name: nameOverride + value: 'vimex-pr-{{number}}' + - name: fullnameOverride + value: 'vimex-pr-{{number}}' + - name: ingress.enabled + value: 'true' + - name: ingress.className + value: 'nginx' + - name: ingress.annotations.nginx\.ingress\.kubernetes\.io/rewrite-target + value: '/' + - name: ingress.hosts[0].host + value: 'pr-{{number}}.vimex.local' + - name: ingress.hosts[0].paths[0].path + value: '/' + - name: ingress.hosts[0].paths[0].pathType + value: 'Prefix' + - name: service.port + value: '5000' + - name: resources.limits.cpu + value: '200m' + - name: resources.limits.memory + value: '256Mi' + - name: resources.requests.cpu + value: '100m' + - name: resources.requests.memory + value: '128Mi' + - name: replicaCount + value: '1' + - name: env[0].name + value: 'ENVIRONMENT' + - name: env[0].value + value: 'pr-{{number}}' + destination: + server: https://kubernetes.default.svc + namespace: 'pr-{{number}}' + syncPolicy: + automated: + prune: true + selfHeal: true + allowEmpty: false + syncOptions: + - Validate=true + - CreateNamespace=true + - PrunePropagationPolicy=foreground + - PruneLast=true + retry: + limit: 5 + backoff: + duration: 5s + factor: 2 + maxDuration: 3m + syncPolicy: + preserveResourcesOnDeletion: false \ No newline at end of file diff --git a/be-flask/README.md b/be-flask/README.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/be-flask/README.md @@ -0,0 +1 @@ +