cd updated to use safer yaml file updating practices #60
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Kubernetes CI/CD | |
| on: | |
| push: | |
| branches: | |
| - main | |
| # pull_request: | |
| # branches: | |
| # - main | |
| # types: | |
| # - closed | |
| workflow_dispatch: | |
| inputs: | |
| environment: | |
| description: 'Select environment' | |
| required: true | |
| default: 'staging' | |
| type: choice | |
| options: | |
| - staging | |
| # currently we do not support cd to production, its only for future reference | |
| - production | |
| env: | |
| CI: false | |
| COMMIT: ${{ github.sha }} | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| jobs: | |
| detect-changes: | |
| # only run this job when a PR is merged or manually triggered | |
| # if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' | |
| name: Detect Changes | |
| runs-on: ubuntu-latest | |
| env: | |
| # select environment based on branch or manual input | |
| ENVIRONMENT: ${{ inputs.environment || (github.ref == 'refs/heads/main' && 'staging') }} | |
| outputs: | |
| frontend_changed: ${{ steps.filter.outputs.frontend }} | |
| backend_changed: ${{ steps.filter.outputs.backend }} | |
| environment: ${{ env.ENVIRONMENT }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Check changed files | |
| id: filter | |
| uses: dorny/paths-filter@v3 | |
| with: | |
| filters: | | |
| frontend: | |
| - 'frontend/**' | |
| backend: | |
| - 'backend/**' | |
| - name: Export change detection outputs # Safely export outputs to env variables | |
| env: | |
| FRONTEND_CHANGED_OUTPUT: ${{ steps.filter.outputs.frontend }} | |
| BACKEND_CHANGED_OUTPUT: ${{ steps.filter.outputs.backend }} | |
| run: | | |
| # Safely write environment variables using printf to avoid template injection | |
| printf "FRONTEND_CHANGED=%q\n" "$FRONTEND_CHANGED_OUTPUT" >> "$GITHUB_ENV" | |
| printf "BACKEND_CHANGED=%q\n" "$BACKEND_CHANGED_OUTPUT" >> "$GITHUB_ENV" | |
| build-and-push: | |
| permissions: | |
| id-token: write | |
| name: Build and Push Docker Images | |
| runs-on: ubuntu-latest | |
| needs: detect-changes | |
| environment: ${{ needs.detect-changes.outputs.environment }} | |
| strategy: | |
| matrix: | |
| service: [frontend, backend] | |
| outputs: | |
| service: ${{ matrix.service }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Configure AWS ECR Details | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: ${{ secrets.AWS_ECR_ROLE }} | |
| aws-region: us-east-1 | |
| - name: Login to Amazon ECR | |
| id: login-ecr | |
| uses: aws-actions/amazon-ecr-login@v2 | |
| with: | |
| mask-password: "true" | |
| - name: Build and push Docker image | |
| id: build-and-push | |
| if: | | |
| (matrix.service == 'frontend' && needs.detect-changes.outputs.frontend_changed == 'true') || | |
| (matrix.service == 'backend' && needs.detect-changes.outputs.backend_changed == 'true') | |
| env: | |
| ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} | |
| ECR_REPOSITORY_PREFIX: ${{ vars.ECR_REPOSITORY_PREFIX }} | |
| run: | | |
| IMAGE_TAG=${COMMIT::7} | |
| SERVICE="${{ matrix.service }}" | |
| echo "Building and pushing $SERVICE image with tag $IMAGE_TAG" | |
| DOCKERFILE_PATH="$SERVICE/Dockerfile" | |
| CONTEXT_DIR="$SERVICE" | |
| ECR_REPOSITORY="$ECR_REPOSITORY_PREFIX/${SERVICE}" | |
| docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f $DOCKERFILE_PATH $CONTEXT_DIR | |
| docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG | |
| update-helm-values: | |
| name: Update Helm Values | |
| needs: [build-and-push, detect-changes] | |
| runs-on: ubuntu-latest | |
| environment: ${{ needs.detect-changes.outputs.environment }} | |
| steps: | |
| - name: Set up SSH for private repo access | |
| uses: webfactory/ssh-agent@v0.9.0 | |
| with: | |
| ssh-private-key: ${{ secrets.DEPLOYMENTS_REPO_WRITE }} | |
| - name: Clone deployments repo (specific branch) | |
| env: | |
| BRANCH_OF_DEPLOYMENT_REPO: ${{ vars.BRANCH_OF_DEPLOYMENT_REPO }} | |
| run: | | |
| git clone --depth=1 --branch $BRANCH_OF_DEPLOYMENT_REPO git@github.com:alpenlabs/deployments.git deployments | |
| cd deployments | |
| git checkout $BRANCH_OF_DEPLOYMENT_REPO | |
| - name: Install yq | |
| run: | | |
| sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/local/bin/yq | |
| sudo chmod +x /usr/local/bin/yq | |
| - name: Update Docker image tag in Helm values # Sanitized SHORT_TAG and safe yq usage | |
| env: | |
| CLUSTER_NAME: ${{ vars.CLUSTER_NAME }} | |
| run: | | |
| # Sanitize and truncate SHORT_TAG | |
| SHORT_TAG="${COMMIT//[^a-zA-Z0-9._-]/}" | |
| SHORT_TAG="${SHORT_TAG:0:7}" | |
| VALUES_FILE="deployments/clusters/$CLUSTER_NAME/values/strata-apps-values.yaml" | |
| # Added boolean validation function | |
| validate_boolean() { [[ "$1" == "true" || "$1" == "false" ]]; } | |
| if validate_boolean "$FRONTEND_CHANGED" && [[ "$FRONTEND_CHANGED" == "true" ]]; then | |
| echo "Updating frontend tag in $VALUES_FILE" | |
| yq eval -i --arg tag "$SHORT_TAG" '.batchExpFe.image.tag = $tag' "$VALUES_FILE" | |
| fi | |
| if validate_boolean "$BACKEND_CHANGED" && [[ "$BACKEND_CHANGED" == "true" ]]; then | |
| echo "Updating backend tag in $VALUES_FILE" | |
| yq eval -i --arg tag "$SHORT_TAG" '.batchExpBe.image.tag = $tag' "$VALUES_FILE" | |
| fi | |
| - name: Commit and push changes | |
| env: | |
| GH_ACTIONS_USER_NAME: ${{ vars.GH_ACTIONS_USER_NAME }} | |
| CLUSTER_NAME: ${{ vars.CLUSTER_NAME }} | |
| BRANCH_OF_DEPLOYMENT_REPO: ${{ vars.BRANCH_OF_DEPLOYMENT_REPO }} | |
| run: | | |
| SHORT_TAG="${COMMIT//[^a-zA-Z0-9._-]/}" | |
| SHORT_TAG="${SHORT_TAG:0:7}" | |
| cd deployments | |
| git config user.name "$GH_ACTIONS_USER_NAME" | |
| git config user.email "$GH_ACTIONS_USER_NAME@alpenlabs.io" | |
| if git diff --quiet; then | |
| echo "No changes to commit." | |
| else | |
| git add clusters/$CLUSTER_NAME/values | |
| git commit -m "Update image tags to $SHORT_TAG for updated services" | |
| git pull --rebase origin $BRANCH_OF_DEPLOYMENT_REPO | |
| git push origin $BRANCH_OF_DEPLOYMENT_REPO | |
| fi |