Provision and Deploy #34
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: Provision and Deploy | |
| on: | |
| workflow_run: | |
| workflows: ["Test, Build and Push Images"] | |
| types: [completed] | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| inputs: | |
| image_tag: | |
| description: "Image tag to deploy (latest or a commit SHA)" | |
| default: latest | |
| # Never let two deploys touch the VM / Terraform state at the same time. | |
| concurrency: | |
| group: deploy-vm | |
| cancel-in-progress: false | |
| permissions: | |
| contents: read | |
| packages: read | |
| jobs: | |
| deploy: | |
| name: Provision and deploy | |
| runs-on: ubuntu-latest | |
| environment: Azure | |
| # Manual runs always proceed; automatic runs only for a green build of main. | |
| if: >- | |
| ${{ github.event_name == 'workflow_dispatch' || | |
| (github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.head_branch == 'main') }} | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v4 | |
| - name: Resolve deployment values | |
| id: vars | |
| run: | | |
| # ghcr.io requires a fully lowercase repository path. | |
| echo "registry=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT" | |
| if [ "${{ github.event_name }}" = "workflow_run" ]; then | |
| echo "image_tag=${{ github.event.workflow_run.head_sha }}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "image_tag=${{ inputs.image_tag }}" >> "$GITHUB_OUTPUT" | |
| fi | |
| # Provision Terraform | |
| - name: Azure login | |
| uses: azure/login@v2 | |
| with: | |
| creds: ${{ secrets.AZURE_CREDENTIALS }} | |
| - name: Set up Terraform | |
| uses: hashicorp/setup-terraform@v3 | |
| with: | |
| terraform_wrapper: false | |
| - name: Terraform apply | |
| working-directory: infra/terraform | |
| run: | | |
| terraform init -input=false | |
| terraform apply -input=false -auto-approve | |
| - name: Capture app URL | |
| id: tf | |
| working-directory: infra/terraform | |
| run: echo "public_ip=$(terraform output -raw public_ip)" >> "$GITHUB_OUTPUT" | |
| # The VM is deallocated between deploys to save cost. Starting it is | |
| # idempotent: it boots a stopped VM and is a no-op if already running. | |
| # Without this, Ansible can't SSH into a powered-off host and the job fails. | |
| - name: Ensure VM is running | |
| working-directory: infra/terraform | |
| run: | | |
| rg="$(terraform output -raw resource_group_name)" | |
| vm="$(terraform output -raw vm_name)" | |
| az vm start --resource-group "$rg" --name "$vm" | |
| # Configure + deploy Ansible | |
| # terraform apply already wrote infra/ansible/inventory.ini + ssh_key.pem. | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.x" | |
| - name: Install Ansible | |
| run: pipx install ansible-core | |
| - name: Write deploy vars | |
| working-directory: infra/ansible | |
| env: | |
| LOGOS_KEY: ${{ secrets.LOGOS_KEY }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| umask 077 | |
| cat > deploy-vars.yml <<EOF | |
| registry: "${{ steps.vars.outputs.registry }}" | |
| image_tag: "${{ steps.vars.outputs.image_tag }}" | |
| ghcr_username: "${{ github.actor }}" | |
| ghcr_token: "${GHCR_TOKEN}" | |
| logos_key: "${LOGOS_KEY}" | |
| openai_api_key: "${OPENAI_API_KEY}" | |
| EOF | |
| - name: Run Ansible playbook | |
| working-directory: infra/ansible | |
| env: | |
| ANSIBLE_HOST_KEY_CHECKING: "False" | |
| run: ansible-playbook site.yml -e @deploy-vars.yml | |
| - name: Clean up secrets | |
| if: always() | |
| working-directory: infra/ansible | |
| run: rm -f deploy-vars.yml ssh_key.pem | |
| - name: Summary | |
| if: success() | |
| run: | | |
| echo "App: http://${{ steps.tf.outputs.public_ip }}:8081" >> "$GITHUB_STEP_SUMMARY" | |
| echo "Prometheus: http://${{ steps.tf.outputs.public_ip }}:9090" >> "$GITHUB_STEP_SUMMARY" | |
| echo "Grafana: http://${{ steps.tf.outputs.public_ip }}:3000" >> "$GITHUB_STEP_SUMMARY" |