Merge pull request #104 from coder/jatcod3r/karpenter-migrate #15
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: "Terraform Deploy" | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - 'infra/**' | |
| - '.github/workflows/terraform-*.yml' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| id-token: write # Required for AWS OIDC | |
| concurrency: | |
| group: terraform-deploy-${{ github.ref }} | |
| cancel-in-progress: false # Don't cancel terraform operations | |
| env: | |
| TERRAFORM_VERSION: "1.15.8" | |
| TERRAGRUNT_VERSION: "0.99.1" | |
| jobs: | |
| discover-changes: | |
| name: Discover Changed Directories | |
| runs-on: ubuntu-latest | |
| outputs: | |
| changed_dirs: ${{ steps.find-changes.outputs.changed_dirs }} | |
| filter_args: ${{ steps.build-filters.outputs.filter_args }} | |
| has_changes: ${{ steps.find-changes.outputs.has_changes }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Find changed directories | |
| id: find-changes | |
| shell: bash | |
| run: | | |
| # For push events, compare with previous commit | |
| if [ "${{ github.event_name }}" = "push" ]; then | |
| BASE_REF="${{ github.event.before }}" | |
| HEAD_REF="${{ github.sha }}" | |
| else | |
| # For workflow_dispatch, we'll scan all directories | |
| echo "Workflow dispatch - will process all directories" | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| echo "changed_dirs=[]" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Find all changed files in infra/ | |
| changed_files=$(git diff --name-only "$BASE_REF" "$HEAD_REF" -- infra/ || echo "") | |
| if [ -z "$changed_files" ]; then | |
| echo "No changes detected in infra/" | |
| echo "has_changes=false" >> $GITHUB_OUTPUT | |
| echo "changed_dirs=[]" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| echo "Changed files:" | |
| echo "$changed_files" | |
| echo "" | |
| # Find directories containing terragrunt.hcl files | |
| terragrunt_dirs=$(find infra -type f -name "terragrunt.hcl" -not -path "*/.terraform/*" | sed 's|/terragrunt.hcl||' | sort -u) | |
| # Identify which Terragrunt directories were affected | |
| changed_dirs="" | |
| while IFS= read -r file; do | |
| file_dir=$(dirname "$file") | |
| # Find the closest parent directory that contains a terragrunt.hcl | |
| while [ "$file_dir" != "." ] && [ "$file_dir" != "infra" ]; do | |
| if echo "$terragrunt_dirs" | grep -q "^$file_dir$"; then | |
| changed_dirs="${changed_dirs}${file_dir}"$'\n' | |
| break | |
| fi | |
| file_dir=$(dirname "$file_dir") | |
| done | |
| done <<< "$changed_files" | |
| # Remove duplicates and sort | |
| changed_dirs=$(echo "$changed_dirs" | sort -u | grep -v '^$' || echo "") | |
| if [ -z "$changed_dirs" ]; then | |
| # Changes were in infra but not in any terragrunt module directory | |
| echo "Changes detected but not in terragrunt module directories" | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| echo "changed_dirs=[]" >> $GITHUB_OUTPUT | |
| else | |
| echo "Changed Terragrunt directories:" | |
| echo "$changed_dirs" | |
| # Strip "infra/" prefix since terragrunt commands run from within infra/ | |
| changed_dirs_relative=$(echo "$changed_dirs" | sed 's|^infra/||') | |
| echo "Relative paths (for filters):" | |
| echo "$changed_dirs_relative" | |
| # Convert to JSON array | |
| changed_dirs_json=$(echo "$changed_dirs_relative" | jq -R -s -c 'split("\n") | map(select(length > 0))') | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| echo "changed_dirs=$changed_dirs_json" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Build filter arguments | |
| id: build-filters | |
| shell: bash | |
| run: | | |
| changed_dirs='${{ steps.find-changes.outputs.changed_dirs }}' | |
| if [ "$changed_dirs" = "[]" ] || [ -z "$changed_dirs" ]; then | |
| echo "filter_args=" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Build --filter arguments for each changed directory | |
| filter_args="" | |
| for dir in $(echo "$changed_dirs" | jq -r '.[]'); do | |
| filter_args="${filter_args} --filter=${dir}" | |
| done | |
| # Trim leading space | |
| filter_args=$(echo "$filter_args" | sed 's/^ //') | |
| echo "Filter arguments: $filter_args" | |
| echo "filter_args=$filter_args" >> $GITHUB_OUTPUT | |
| apply: | |
| name: Apply Infrastructure | |
| needs: discover-changes | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: production | |
| url: https://ai.coder.com | |
| env: | |
| # Repository Variables | |
| TERRAGRUNT_ARGS: ${{ vars.TERRAGRUNT_ARGS }} | |
| TERRAFORM_INIT_ARGS: ${{ vars.TERRAFORM_INIT_ARGS }} | |
| TERRAFORM_APPLY_ARGS: ${{ vars.TERRAFORM_APPLY_ARGS }} | |
| CODER_TF_BACKEND_AWS_BUCKET_NAME: ${{ vars.CODER_TF_BACKEND_AWS_BUCKET_NAME }} | |
| CODER_TF_BACKEND_AWS_REGION: ${{ vars.CODER_TF_BACKEND_AWS_REGION }} | |
| CODER_TF_BACKEND_AWS_PROFILE: ${{ vars.CODER_TF_BACKEND_AWS_PROFILE }} | |
| CODER_TF_BACKEND_ENCRYPT: ${{ vars.CODER_TF_BACKEND_ENCRYPT }} | |
| CODER_AWS_PROFILE: ${{ vars.CODER_AWS_PROFILE }} | |
| CODER_AWS_REGION: ${{ vars.CODER_AWS_REGION }} | |
| CODER_AWS_AZS: ${{ vars.CODER_AWS_AZS }} | |
| CODER_CLUSTER_NAME: ${{ vars.CODER_CLUSTER_NAME }} | |
| CODER_CLUSTER_VERSION: ${{ vars.CODER_CLUSTER_VERSION }} | |
| CODER_CLUSTER_INSTANCE_TYPE: ${{ vars.CODER_CLUSTER_INSTANCE_TYPE }} | |
| CODER_VPC_NAME: ${{ vars.CODER_VPC_NAME }} | |
| CODER_VPC_CIDR: ${{ vars.CODER_VPC_CIDR }} | |
| CODER_VPC_AZS: ${{ vars.CODER_VPC_AZS }} | |
| CODER_VPC_NAT_NAME: ${{ vars.CODER_VPC_NAT_NAME }} | |
| CODER_DB_SUBNET_GROUP_NAME: ${{ vars.CODER_DB_SUBNET_GROUP_NAME }} | |
| CODER_PUBLIC_SUBNET_SUFFIX: ${{ vars.CODER_PUBLIC_SUBNET_SUFFIX }} | |
| CODER_PRIVATE_SUBNET_SUFFIX: ${{ vars.CODER_PRIVATE_SUBNET_SUFFIX }} | |
| CODER_DB_RDS_ID: ${{ vars.CODER_DB_RDS_ID }} | |
| CODER_DB_USERNAME: ${{ vars.CODER_DB_USERNAME }} | |
| CODER_DB_NAME: ${{ vars.CODER_DB_NAME }} | |
| CODER_IMAGE_REPO: ${{ vars.CODER_IMAGE_REPO }} | |
| CODER_IMAGE_TAG: ${{ vars.CODER_IMAGE_TAG }} | |
| LOKI_S3_BUCKET_NAME: ${{ vars.LOKI_S3_BUCKET_NAME }} | |
| LOKI_S3_BUCKET_REGION: ${{ vars.LOKI_S3_BUCKET_REGION }} | |
| GRAFANA_DOMAIN_NAME: ${{ vars.GRAFANA_DOMAIN_NAME }} | |
| GRAFANA_DB_USERNAME: ${{ vars.GRAFANA_DB_USERNAME }} | |
| CODER_DOMAIN_NAME: ${{ vars.CODER_DOMAIN_NAME }} | |
| CODER_WILDCARD_URL: ${{ vars.CODER_WILDCARD_URL }} | |
| CODER_LICENSE: ${{ vars.CODER_LICENSE }} | |
| CODER_EXPERIMENTS: ${{ vars.CODER_EXPERIMENTS }} | |
| CODER_BUILT_IN_PROVISIONER_COUNT: ${{ vars.CODER_BUILT_IN_PROVISIONER_COUNT }} | |
| CODER_OIDC_SIGN_IN_TEXT: ${{ vars.CODER_OIDC_SIGN_IN_TEXT }} | |
| CODER_OIDC_ICON_URL: ${{ vars.CODER_OIDC_ICON_URL }} | |
| CODER_OIDC_SCOPES: ${{ vars.CODER_OIDC_SCOPES }} | |
| CODER_OIDC_EMAIL_DOMAIN: ${{ vars.CODER_OIDC_EMAIL_DOMAIN }} | |
| CODER_OIDC_ISSUER_URL: ${{ vars.CODER_OIDC_ISSUER_URL }} | |
| # Secrets | |
| CF_EMAIL: ${{ secrets.CF_EMAIL }} | |
| CF_TOKEN: ${{ secrets.CF_TOKEN }} | |
| CODER_GITHUB_EXTERN_AUTH_CLIENT_ID: ${{ secrets.CODER_GITHUB_EXTERN_AUTH_CLIENT_ID }} | |
| CODER_GITHUB_EXTERN_AUTH_CLIENT_SECRET: ${{ secrets.CODER_GITHUB_EXTERN_AUTH_CLIENT_SECRET }} | |
| CODER_GITHUB_OAUTH_CLIENT_ID: ${{ secrets.CODER_GITHUB_OAUTH_CLIENT_ID }} | |
| CODER_GITHUB_OAUTH_CLIENT_SECRET: ${{ secrets.CODER_GITHUB_OAUTH_CLIENT_SECRET }} | |
| CODER_OIDC_CLIENT_ID: ${{ secrets.CODER_OIDC_CLIENT_ID }} | |
| CODER_OIDC_CLIENT_SECRET: ${{ secrets.CODER_OIDC_CLIENT_SECRET }} | |
| CODER_RUNNER_TOKEN: ${{ secrets.CODER_RUNNER_TOKEN }} | |
| GRAFANA_USER_PASSWORD: ${{ secrets.GRAFANA_USER_PASSWORD }} | |
| VANTAGE_API_TOKEN: ${{ secrets.VANTAGE_API_TOKEN }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Terraform | |
| uses: hashicorp/setup-terraform@v3 | |
| with: | |
| terraform_version: ${{ env.TERRAFORM_VERSION }} | |
| terraform_wrapper: false | |
| - name: Setup Terragrunt | |
| uses: gruntwork-io/terragrunt-action@v3 | |
| with: | |
| tf_path: "terraform" | |
| tg_version: ${{ env.TERRAGRUNT_VERSION }} | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v6.2.3 | |
| with: | |
| role-to-assume: ${{ vars.AWS_RUNNER_ROLE_ARN }} | |
| aws-region: ${{ vars.AWS_RUNNER_REGION }} | |
| aws-profile: ${{ vars.CODER_AWS_PROFILE }} | |
| - name: Terragrunt init | |
| shell: bash | |
| working-directory: infra | |
| run: | | |
| echo "::group::Initializing all modules" | |
| FILTER_ARGS="${{ needs.discover-changes.outputs.filter_args }}" | |
| # Build the full command with filters | |
| CMD="terragrunt run --all --queue-exclude-dir='./' --provider-cache" | |
| if [ -n "$FILTER_ARGS" ]; then | |
| CMD="$CMD $FILTER_ARGS" | |
| echo "Applying filters: $FILTER_ARGS" | |
| else | |
| echo "No filters - processing all modules" | |
| fi | |
| if [ -n "${TERRAGRUNT_ARGS}" ]; then | |
| CMD="$CMD ${TERRAGRUNT_ARGS}" | |
| fi | |
| CMD="$CMD -- init -input=false -reconfigure -upgrade" | |
| if [ -n "${TERRAFORM_INIT_ARGS}" ]; then | |
| CMD="$CMD ${TERRAFORM_INIT_ARGS}" | |
| fi | |
| echo "Running: $CMD" | |
| eval "$CMD" | |
| echo "✓ Initialization successful" | |
| echo "::endgroup::" | |
| - name: Terragrunt run-all apply | |
| shell: bash | |
| working-directory: infra | |
| run: | | |
| echo "::group::Running Terragrunt run-all apply" | |
| FILTER_ARGS="${{ needs.discover-changes.outputs.filter_args }}" | |
| # Build the full command with filters | |
| CMD="terragrunt run --all --queue-exclude-dir='./' --provider-cache" | |
| if [ -n "$FILTER_ARGS" ]; then | |
| CMD="$CMD $FILTER_ARGS" | |
| echo "Applying filters: $FILTER_ARGS" | |
| else | |
| echo "No filters - processing all modules" | |
| fi | |
| if [ -n "${TERRAGRUNT_ARGS}" ]; then | |
| CMD="$CMD ${TERRAGRUNT_ARGS}" | |
| fi | |
| CMD="$CMD -- apply -input=false -auto-approve" | |
| if [ -n "${TERRAFORM_APPLY_ARGS}" ]; then | |
| CMD="$CMD ${TERRAFORM_APPLY_ARGS}" | |
| fi | |
| echo "Running: $CMD" | |
| eval "$CMD" 2>&1 | tee apply.log | |
| EXIT_CODE=${PIPESTATUS[0]} | |
| echo "::endgroup::" | |
| if [[ $EXIT_CODE -eq 0 ]]; then | |
| echo "✅ Apply succeeded" | |
| else | |
| echo "❌ Apply failed" | |
| exit $EXIT_CODE | |
| fi | |
| - name: Upload apply log | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: apply-log | |
| path: infra/apply.log | |
| retention-days: 90 | |
| summary: | |
| name: Deployment Summary | |
| needs: [discover-changes, apply] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check deployment results | |
| shell: bash | |
| run: | | |
| echo "## Terraform Deployment Summary" | |
| echo "" | |
| changed_dirs='${{ needs.discover-changes.outputs.changed_dirs }}' | |
| has_changes="${{ needs.discover-changes.outputs.has_changes }}" | |
| apply_result="${{ needs.apply.result }}" | |
| # Display changed directories | |
| if [ "$has_changes" = "true" ] && [ "$changed_dirs" != "[]" ] && [ -n "$changed_dirs" ]; then | |
| echo "Deployed Terragrunt directories:" | |
| echo "$changed_dirs" | jq -r '.[]' | sed 's/^/ - /' | |
| echo "" | |
| elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| echo "Manual deployment - all modules processed" | |
| echo "" | |
| elif [ "$has_changes" = "true" ]; then | |
| echo "Changes detected in infra/ but not in terragrunt module directories" | |
| echo "" | |
| else | |
| echo "No changes detected in infra/" | |
| echo "" | |
| fi | |
| echo "Deployment result: $apply_result" | |
| echo "" | |
| if [[ "$apply_result" == "success" ]]; then | |
| echo "✅ Deployment succeeded!" | |
| echo "" | |
| echo "Infrastructure changes have been applied to production." | |
| exit 0 | |
| else | |
| echo "❌ Deployment failed" | |
| echo "" | |
| echo "Please check the logs and resolve any issues." | |
| exit 1 | |
| fi |