Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: CD

on:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read

jobs:
deploy:
name: deploy (Ansible)
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- uses: actions/checkout@v4

- name: Install Ansible
run: |
python -m pip install --upgrade pip
pip install ansible

- name: Write SSH private key
run: |
mkdir -p ~/.ssh
printf '%s\n' "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key

- name: Write genai .env file
env:
GENAI_ENV_CONTENT: ${{ secrets.GENAI_ENV_CONTENT }}
run: printf '%s\n' "$GENAI_ENV_CONTENT" > /tmp/genai.env

- name: Create inventory
run: |
cat > /tmp/inventory.yml << INVENTORY
all:
hosts:
team_devoops_vm:
ansible_host: ${{ secrets.VM_HOST }}
ansible_user: azureuser
ansible_ssh_private_key_file: ~/.ssh/deploy_key
ansible_ssh_common_args: "-o StrictHostKeyChecking=no"
INVENTORY

- name: Run Ansible playbook
run: |
ansible-playbook \
-i /tmp/inventory.yml \
infra/ansible/playbook.yml \
-e "repo_url=https://github.com/${{ github.repository }}.git" \
-e "genai_env_file=/tmp/genai.env"
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ jobs:
run: cp services/py-genai-helper/.env.example services/py-genai-helper/.env

- name: Build all images via docker compose
run: docker compose build
run: docker compose -f infra/docker-compose.yml build

- name: Verify expected image tags exist
# Compose tags images as `<project>-<service>:latest`; project name
Expand Down
75 changes: 75 additions & 0 deletions .github/workflows/infra.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Infra (Terraform)

on:
workflow_dispatch:
inputs:
action:
description: Terraform action to run
required: true
default: plan
type: choice
options:
- plan
- apply
- destroy

# OIDC: allows the job to exchange a GitHub token for an Azure access token.
# No client secret is required.
permissions:
id-token: write
contents: read

jobs:
terraform:
name: terraform (${{ inputs.action }})
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: infra/terraform
env:
ARM_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }}
ARM_TENANT_ID: ${{ vars.AZURE_TENANT_ID }}
ARM_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}
ARM_USE_OIDC: "true"
ARM_USE_AZUREAD: "true"

steps:
- uses: actions/checkout@v4

- name: Azure login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

- name: Setup Terraform
uses: hashicorp/setup-terraform@v3

- name: Write tfvars
env:
SSH_PUBLIC_KEY: ${{ secrets.VM_SSH_PUBLIC_KEY }}
run: printf 'admin_ssh_public_key = "%s"\n' "$SSH_PUBLIC_KEY" > terraform.tfvars

- name: Terraform init
run: terraform init

- name: Terraform plan
if: inputs.action == 'plan' || inputs.action == 'apply'
run: terraform plan -out=tfplan

- name: Terraform apply
if: inputs.action == 'apply'
run: terraform apply -auto-approve tfplan

- name: Show VM public IP
if: inputs.action == 'apply'
run: |
ip=$(terraform output -raw vm_public_ip)
echo "::notice::VM public IP: $ip"
echo "Add this as the VM_HOST secret in the GitHub 'production' environment."

- name: Terraform destroy
if: inputs.action == 'destroy'
run: terraform destroy -auto-approve
1 change: 1 addition & 0 deletions infra/ansible/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
inventory.yml
4 changes: 4 additions & 0 deletions infra/ansible/ansible.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[defaults]
host_key_checking = False
remote_user = azureuser
stdout_callback = yaml
9 changes: 9 additions & 0 deletions infra/ansible/inventory.yml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copy this file to inventory.yml (gitignored) and fill in the VM's public IP.
# Get the IP after terraform apply:
# terraform -chdir=infra/terraform output -raw vm_public_ip
all:
hosts:
team_devoops_vm:
ansible_host: <VM_PUBLIC_IP>
ansible_user: azureuser
ansible_ssh_private_key_file: ~/.ssh/id_ed25519
91 changes: 91 additions & 0 deletions infra/ansible/playbook.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
- name: Deploy team-devoops to Azure VM
hosts: all
become: true
vars:
app_dir: /opt/team-devoops
repo_url: https://github.com/your-org/team-devoops.git # override via -e
branch: main

tasks:
# --------------------------------------------------------------------------
# Docker installation (idempotent)
# --------------------------------------------------------------------------
- name: Install prerequisite packages
apt:
name:
- ca-certificates
- curl
- gnupg
- git
state: present
update_cache: true

- name: Create /etc/apt/keyrings directory
file:
path: /etc/apt/keyrings
state: directory
mode: "0755"

- name: Download Docker GPG key
get_url:
url: https://download.docker.com/linux/ubuntu/gpg
dest: /etc/apt/keyrings/docker.asc
mode: "0644"
force: false

- name: Add Docker apt repository
apt_repository:
repo: >-
deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc]
https://download.docker.com/linux/ubuntu
{{ ansible_distribution_release }} stable
state: present
filename: docker

- name: Install Docker packages
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
state: present
update_cache: true

- name: Add admin user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: true

- name: Start and enable Docker
systemd:
name: docker
state: started
enabled: true

# --------------------------------------------------------------------------
# Application deployment
# --------------------------------------------------------------------------
- name: Clone or update repository
git:
repo: "{{ repo_url }}"
dest: "{{ app_dir }}"
version: "{{ branch }}"
force: true
update: true

- name: Write py-genai-helper .env file
copy:
src: "{{ genai_env_file }}"
dest: "{{ app_dir }}/services/py-genai-helper/.env"
mode: "0600"

- name: Deploy with docker compose
shell: >
docker compose -f infra/docker-compose.yml up -d --build --remove-orphans
args:
chdir: "{{ app_dir }}"
environment:
COMPOSE_HTTP_TIMEOUT: "120"
Loading
Loading