diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 7eefe44..d004b99 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -5,6 +5,7 @@ This document explains the modular deployment and destroy workflows for the Alpi ## Overview The deployment and destroy processes have been modularized into distinct, triggerable workflows. This allows for: + - **Efficiency**: Only deploy what you need (backend, infrastructure, or application) - **Flexibility**: Run workflows independently or orchestrated together - **Cost Optimization**: Avoid unnecessary operations (e.g., running Terraform when only frontend changes) @@ -14,14 +15,17 @@ The deployment and destroy processes have been modularized into distinct, trigge ### Deployment Workflows #### 1. `deploy-backend.yaml` - Backend Storage Deployment + **Purpose**: Creates the remote Terraform state storage in Azure (Blob Storage) **When to use**: + - First-time setup for a new environment - Rarely needed after initial setup - Only if backend storage was accidentally deleted **What it creates**: + - Azure Resource Group: `{env}-bkd-alpinebot` - Azure Storage Account: `{env}bkdalpinebotsa` - Azure Storage Container: `{env}-bkd-alpinebot-co` @@ -29,14 +33,17 @@ The deployment and destroy processes have been modularized into distinct, trigge **Trigger**: Manual (workflow_dispatch) #### 2. `deploy-infra.yaml` - Infrastructure Deployment + **Purpose**: Deploys all infrastructure using Terraform **When to use**: + - When infrastructure code changes (modules, main.tf, variables, etc.) - When scaling resources - When adding/removing Azure services **What it deploys**: + - Azure Resource Group - Key Vault - OpenAI Cognitive Account @@ -52,14 +59,17 @@ The deployment and destroy processes have been modularized into distinct, trigge **Trigger**: Manual (workflow_dispatch) #### 3. `deploy-app.yaml` - Application Deployment + **Purpose**: Builds and deploys only the frontend React application **When to use**: + - On every frontend code change - When updating UI/UX - Most frequent deployment workflow **What it does**: + - Installs npm dependencies - Builds React application - Deploys build artifacts to Azure Web App @@ -69,14 +79,17 @@ The deployment and destroy processes have been modularized into distinct, trigge **Trigger**: Manual (workflow_dispatch) #### 4. `deploy.yaml` - Full Environment Orchestrator + **Purpose**: Orchestrates backend, infrastructure, and application deployment in sequence **When to use**: + - Complete environment setup - When you want to control which components to deploy - Backward compatibility with old workflow **Features**: + - Optional backend deployment (default: false) - Optional infrastructure deployment (default: true) - Optional application deployment (default: true) @@ -87,14 +100,17 @@ The deployment and destroy processes have been modularized into distinct, trigge ### Destroy Workflows #### 1. `destroy-app.yaml` - Application Destroy + **Purpose**: Stops the Web App deployment **When to use**: + - To stop the running application without destroying infrastructure - Temporary shutdown to save costs - Before redeploying a fresh build **What it does**: + - Stops the Azure Web App **Note**: Does not delete the App Service resource, only stops it @@ -102,14 +118,17 @@ The deployment and destroy processes have been modularized into distinct, trigge **Trigger**: Manual (workflow_dispatch) #### 2. `destroy-infra.yaml` - Infrastructure Destroy + **Purpose**: Destroys all Terraform-managed infrastructure **When to use**: + - Tearing down an environment - Before major infrastructure changes - Cleanup after testing **What it destroys**: + - All resources created by Terraform - Does NOT destroy backend storage (state remains safe) @@ -118,13 +137,16 @@ The deployment and destroy processes have been modularized into distinct, trigge **Trigger**: Manual (workflow_dispatch) #### 3. `destroy-backend.yaml` - Backend Storage Destroy + **Purpose**: Destroys the backend Terraform state storage **When to use**: + - Complete environment teardown - WARNING: This deletes your Terraform state! **What it destroys**: + - Storage Container - Storage Account - Resource Group @@ -136,13 +158,16 @@ The deployment and destroy processes have been modularized into distinct, trigge **Trigger**: Manual (workflow_dispatch) #### 4. `destroy.yaml` - Full Environment Orchestrator + **Purpose**: Orchestrates destruction of app, infrastructure, and backend in sequence **When to use**: + - Complete environment teardown - When you want control over what to destroy **Features**: + - Optional app destroy (default: true) - Optional infrastructure destroy (default: true) - Optional backend destroy (default: false) - protected by default @@ -153,6 +178,7 @@ The deployment and destroy processes have been modularized into distinct, trigge ## Usage Examples ### Scenario 1: First-Time Environment Setup + ``` 1. Run: deploy-backend.yaml (env: dev) 2. Run: deploy-infra.yaml (env: dev) @@ -160,6 +186,7 @@ The deployment and destroy processes have been modularized into distinct, trigge ``` Or use the orchestrator: + ``` 1. Run: deploy.yaml - environment: dev @@ -169,17 +196,20 @@ Or use the orchestrator: ``` ### Scenario 2: Frontend Code Change + ``` 1. Run: deploy-app.yaml (env: dev) ``` ### Scenario 3: Infrastructure Update + ``` 1. Run: deploy-infra.yaml (env: dev) 2. Run: deploy-app.yaml (env: dev) # Re-deploy app if needed ``` ### Scenario 4: Complete Environment Teardown + ``` 1. Run: destroy-app.yaml (env: dev) 2. Run: destroy-infra.yaml (env: dev) @@ -187,6 +217,7 @@ Or use the orchestrator: ``` Or use the orchestrator: + ``` 1. Run: destroy.yaml - environment: dev @@ -196,6 +227,7 @@ Or use the orchestrator: ``` ### Scenario 5: Temporary Shutdown + ``` 1. Run: destroy-app.yaml (env: dev) # Infrastructure remains, just the app is stopped @@ -204,6 +236,7 @@ Or use the orchestrator: ## Environment Support All workflows support three environments: + - `dev` - Development environment - `qa` - Quality Assurance/Testing environment - `main` - Production environment @@ -211,6 +244,7 @@ All workflows support three environments: ## Required Secrets All workflows require these GitHub secrets to be configured: + - `AZURE_CLIENT_ID` - Azure Service Principal Client ID - `AZURE_TENANT_ID` - Azure Tenant ID - `AZURE_SUBSCRIPTION_ID` - Azure Subscription ID @@ -220,8 +254,6 @@ All workflows require these GitHub secrets to be configured: - `POSTGRESQL_ADMIN_PASSWORD` - PostgreSQL admin password - `GOOGLE_CLIENT_ID` - Google OAuth Client ID - `GOOGLE_CLIENT_SECRET` - Google OAuth Client Secret -- `MICROSOFT_CLIENT_ID` - Microsoft OAuth Client ID -- `MICROSOFT_CLIENT_SECRET` - Microsoft OAuth Client Secret ## Best Practices @@ -236,19 +268,23 @@ All workflows require these GitHub secrets to be configured: ## Troubleshooting ### Backend already exists error + - This is normal if backend was already created - Skip the backend deployment step or use orchestrator with `deploy_backend: false` ### Terraform state lock error + - Wait for other Terraform operations to complete - Check if another workflow is running ### Web App not found during destroy + - The resource may already be deleted - Check Azure Portal to verify - This is generally safe to ignore ### Permission errors + - Verify all required secrets are configured - Check Azure Service Principal permissions - Ensure OIDC federation is properly configured @@ -256,15 +292,18 @@ All workflows require these GitHub secrets to be configured: ## Migration from Old Workflows The old monolithic workflows have been replaced with: + - `deploy.yaml` - Now an orchestrator with options - `destroy.yaml` - Now an orchestrator with options **Key differences**: + - More granular control over what gets deployed/destroyed - Optional components via boolean inputs - Better efficiency by skipping unnecessary steps **Backward compatibility**: + - The orchestrator workflows maintain similar behavior - Can still deploy full environment in one run - Choose which components to deploy/destroy diff --git a/.github/workflows/deploy-function.yaml b/.github/workflows/deploy-function.yaml new file mode 100644 index 0000000..87d6fbe --- /dev/null +++ b/.github/workflows/deploy-function.yaml @@ -0,0 +1,63 @@ +name: Deploy Function App + +on: + workflow_dispatch: + inputs: + environment: + description: 'Environment to deploy function app to' + required: true + type: choice + options: + - dev + - qa + - main + +permissions: + id-token: write + contents: read + +jobs: + deploy-function: + name: Deploy Azure Function App + runs-on: ubuntu-latest + environment: ${{ github.event.inputs.environment }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set environment + id: setenv + run: | + echo "ENVIRONMENT=${{ github.event.inputs.environment }}" >> $GITHUB_ENV + echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT + + - name: Login To Azure Using OIDC + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + allow-no-subscriptions: false + enable-AzPSSession: false + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install Dependencies + run: | + cd backend + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Deploy to Azure Function App + uses: Azure/functions-action@v1 + with: + app-name: ${{ env.ENVIRONMENT }}-alpinebot-func + package: ./backend + + - name: Output deployment information + run: | + echo "Function App deployed successfully to: ${{ env.ENVIRONMENT }}-alpinebot-func" + echo "Function URL: https://${{ env.ENVIRONMENT }}-alpinebot-func.azurewebsites.net" diff --git a/.github/workflows/deploy-infra.yaml b/.github/workflows/deploy-infra.yaml index 8c41718..e269d19 100644 --- a/.github/workflows/deploy-infra.yaml +++ b/.github/workflows/deploy-infra.yaml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: environment: - description: 'Environment to deploy infrastructure to' + description: "Environment to deploy infrastructure to" required: true type: choice options: @@ -52,8 +52,6 @@ jobs: echo "TF_VAR_postgresql_admin_username=${{ secrets.POSTGRESQL_ADMIN_USERNAME }}" >> $GITHUB_ENV echo "TF_VAR_google_client_id=${{ secrets.GOOGLE_CLIENT_ID }}" >> $GITHUB_ENV echo "TF_VAR_google_client_secret=${{ secrets.GOOGLE_CLIENT_SECRET }}" >> $GITHUB_ENV - echo "TF_VAR_microsoft_client_id=${{ secrets.MICROSOFT_CLIENT_ID }}" >> $GITHUB_ENV - echo "TF_VAR_microsoft_client_secret=${{ secrets.MICROSOFT_CLIENT_SECRET }}" >> $GITHUB_ENV - name: Set Up Terraform uses: hashicorp/setup-terraform@v2 diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 47ea24e..53ae9f7 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: environment: - description: 'Environment to deploy to' + description: "Environment to deploy to" required: true type: choice options: @@ -12,17 +12,22 @@ on: - qa - main deploy_backend: - description: 'Deploy backend storage (usually only needed on first deployment)' + description: "Deploy backend storage (usually only needed on first deployment)" required: false type: boolean default: false deploy_infra: - description: 'Deploy infrastructure (Terraform)' + description: "Deploy infrastructure (Terraform)" + required: false + type: boolean + default: true + deploy_function: + description: "Deploy Function App (Backend)" required: false type: boolean default: true deploy_app: - description: 'Deploy application (Frontend)' + description: "Deploy application (Frontend)" required: false type: boolean default: true @@ -139,8 +144,6 @@ jobs: echo "TF_VAR_postgresql_admin_username=${{ secrets.POSTGRESQL_ADMIN_USERNAME }}" >> $GITHUB_ENV echo "TF_VAR_google_client_id=${{ secrets.GOOGLE_CLIENT_ID }}" >> $GITHUB_ENV echo "TF_VAR_google_client_secret=${{ secrets.GOOGLE_CLIENT_SECRET }}" >> $GITHUB_ENV - echo "TF_VAR_microsoft_client_id=${{ secrets.MICROSOFT_CLIENT_ID }}" >> $GITHUB_ENV - echo "TF_VAR_microsoft_client_secret=${{ secrets.MICROSOFT_CLIENT_SECRET }}" >> $GITHUB_ENV - name: Set Up Terraform uses: hashicorp/setup-terraform@v2 @@ -186,12 +189,59 @@ jobs: run: | echo "Infrastructure deployment completed for environment: ${{ env.ENVIRONMENT }}" + deploy-function: + name: Deploy Function App + if: ${{ always() && github.event.inputs.deploy_function == 'true' && (needs.deploy-infra.result == 'success' || needs.deploy-infra.result == 'skipped') }} + runs-on: ubuntu-latest + environment: ${{ github.event.inputs.environment }} + needs: deploy-infra + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set environment + id: setenv + run: | + echo "ENVIRONMENT=${{ github.event.inputs.environment }}" >> $GITHUB_ENV + echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT + + - name: Login To Azure Using OIDC + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + allow-no-subscriptions: false + enable-AzPSSession: false + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install Dependencies + run: | + cd backend + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Deploy to Azure Function App + uses: Azure/functions-action@v1 + with: + app-name: ${{ env.ENVIRONMENT }}-alpinebot-func + package: ./backend + + - name: Output deployment information + run: | + echo "Function App deployed successfully to: ${{ env.ENVIRONMENT }}-alpinebot-func" + echo "Function URL: https://${{ env.ENVIRONMENT }}-alpinebot-func.azurewebsites.net" + deploy-app: name: Deploy Application - if: ${{ always() && github.event.inputs.deploy_app == 'true' && (needs.deploy-infra.result == 'success' || needs.deploy-infra.result == 'skipped') }} + if: ${{ always() && github.event.inputs.deploy_app == 'true' && (needs.deploy-function.result == 'success' || needs.deploy-function.result == 'skipped') }} runs-on: ubuntu-latest environment: ${{ github.event.inputs.environment }} - needs: deploy-infra + needs: deploy-function steps: - name: Checkout code uses: actions/checkout@v4 @@ -214,8 +264,8 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v4 with: - node-version: '18' - cache: 'npm' + node-version: "18" + cache: "npm" cache-dependency-path: frontend/app/package.json - name: Install Frontend Dependencies @@ -234,4 +284,4 @@ jobs: - name: Output deployment information run: | - echo "Frontend application deployed successfully to: ${{ env.ENVIRONMENT }}-alpinebot-as" \ No newline at end of file + echo "Frontend application deployed successfully to: ${{ env.ENVIRONMENT }}-alpinebot-as" diff --git a/.github/workflows/destroy-infra.yaml b/.github/workflows/destroy-infra.yaml index 6c2f54d..3fe42b2 100644 --- a/.github/workflows/destroy-infra.yaml +++ b/.github/workflows/destroy-infra.yaml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: environment: - description: 'Environment to destroy infrastructure for' + description: "Environment to destroy infrastructure for" required: true type: choice options: @@ -50,8 +50,6 @@ jobs: echo "TF_VAR_postgresql_admin_username=${{ secrets.POSTGRESQL_ADMIN_USERNAME }}" >> $GITHUB_ENV echo "TF_VAR_google_client_id=${{ secrets.GOOGLE_CLIENT_ID }}" >> $GITHUB_ENV echo "TF_VAR_google_client_secret=${{ secrets.GOOGLE_CLIENT_SECRET }}" >> $GITHUB_ENV - echo "TF_VAR_microsoft_client_id=${{ secrets.MICROSOFT_CLIENT_ID }}" >> $GITHUB_ENV - echo "TF_VAR_microsoft_client_secret=${{ secrets.MICROSOFT_CLIENT_SECRET }}" >> $GITHUB_ENV - name: Set up Terraform uses: hashicorp/setup-terraform@v2 diff --git a/.github/workflows/destroy.yaml b/.github/workflows/destroy.yaml index 26d5920..e318e7b 100644 --- a/.github/workflows/destroy.yaml +++ b/.github/workflows/destroy.yaml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: environment: - description: 'Environment to destroy' + description: "Environment to destroy" required: true type: choice options: @@ -12,17 +12,17 @@ on: - qa - main destroy_app: - description: 'Destroy application deployment (stop Web App)' + description: "Destroy application deployment (stop Web App)" required: false type: boolean default: true destroy_infra: - description: 'Destroy infrastructure (Terraform resources)' + description: "Destroy infrastructure (Terraform resources)" required: false type: boolean default: true destroy_backend: - description: 'Destroy backend storage (WARNING: deletes Terraform state!)' + description: "Destroy backend storage (WARNING: deletes Terraform state!)" required: false type: boolean default: false @@ -59,10 +59,10 @@ jobs: run: | az_webapp_name="${{ env.ENVIRONMENT }}-alpinebot-as" echo "Stopping web app: $az_webapp_name" - + # Get resource group name for the web app az_rg_name=$(az webapp show --name $az_webapp_name --query resourceGroup -o tsv 2>/dev/null || echo "") - + if [ -n "$az_rg_name" ]; then az webapp stop --name $az_webapp_name --resource-group $az_rg_name echo "Web app stopped successfully" @@ -109,8 +109,6 @@ jobs: echo "TF_VAR_postgresql_admin_username=${{ secrets.POSTGRESQL_ADMIN_USERNAME }}" >> $GITHUB_ENV echo "TF_VAR_google_client_id=${{ secrets.GOOGLE_CLIENT_ID }}" >> $GITHUB_ENV echo "TF_VAR_google_client_secret=${{ secrets.GOOGLE_CLIENT_SECRET }}" >> $GITHUB_ENV - echo "TF_VAR_microsoft_client_id=${{ secrets.MICROSOFT_CLIENT_ID }}" >> $GITHUB_ENV - echo "TF_VAR_microsoft_client_secret=${{ secrets.MICROSOFT_CLIENT_SECRET }}" >> $GITHUB_ENV - name: Set up Terraform uses: hashicorp/setup-terraform@v2 diff --git a/.gitignore b/.gitignore index 661d08c..691dee9 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,15 @@ override.tf.json **/dist/ # Package lock files (optional - remove if you want to commit them) -package-lock.json +# package-lock.json + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +*.egg-info/ +.venv/ +venv/ +ENV/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c00196f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,32 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Initial project setup +- Basic functionality + +### Changed + +### Deprecated + +### Removed + +### Fixed + +### Security + +## [0.1.0] - YYYY-MM-DD + +### Added + +- Initial release + +[Unreleased]: https://github.com/yourusername/alpinebot/compare/v0.1.0...HEAD +[0.1.0]: https://github.com/yourusername/alpinebot/releases/tag/v0.1.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ab98414 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,160 @@ +# Contributing to AlpineBot + +First off, thank you for considering contributing to AlpineBot! 🇨🇭 + +Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue, assessing changes, and helping you finalize your pull requests. + +## Code of Conduct + +By participating in this project, you are expected to uphold our Code of Conduct: be respectful, inclusive, and constructive in all interactions. + +## How Can I Contribute? + +### Reporting Bugs + +Before creating bug reports, please check the existing issues to avoid duplicates. When you create a bug report, include as many details as possible: + +- **Use a clear and descriptive title** +- **Describe the exact steps to reproduce the problem** +- **Provide specific examples** (code snippets, screenshots, etc.) +- **Describe the behavior you observed and what you expected** +- **Include your environment details** (OS, browser, Node version, etc.) + +### Suggesting Enhancements + +Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion: + +- **Use a clear and descriptive title** +- **Provide a detailed description** of the suggested enhancement +- **Explain why this enhancement would be useful** +- **List any alternative solutions** you've considered + +### Pull Requests + +1. **Fork the repository** and create your branch from `dev` +2. **Follow the Test-Driven Development (TDD) approach**: + - Write tests before implementing features + - Ensure all tests pass before submitting +3. **Follow the coding style** of the project +4. **Update documentation** if you change functionality +5. **Write clear commit messages** following conventional commits format +6. **Ensure your PR description clearly describes the problem and solution** + +## Development Process + +### Branching Strategy + +- `main` - production-ready code +- `qa` - quality assurance/staging environment +- `dev` - active development branch +- `feature/*` - feature branches (branch from `dev`) +- `bugfix/*` - bug fix branches (branch from `dev`) + +### Setting Up Your Development Environment + +```bash +# Clone your fork +git clone https://github.com/YOUR_USERNAME/alpinebot.git +cd alpinebot + +# Add upstream remote +git remote add upstream https://github.com/fpittelo/alpinebot.git + +# Create a feature branch +git checkout -b feature/your-feature-name dev +``` + +### Test-Driven Development (TDD) + +This project strictly follows TDD: + +1. **Write a failing test** that defines the desired functionality +2. **Write the minimum code** needed to make the test pass +3. **Refactor** the code while keeping tests green +4. **Repeat** for each new feature or bug fix + +### Running Tests + +```bash +# Frontend tests +cd frontend +npm test + +# Backend tests +cd backend +pytest +``` + +### Deployment + +**Important**: All infrastructure deployment is handled via GitHub Actions. Do NOT run Terraform locally. + +- Pushing to `dev` triggers dev environment deployment +- PRs to `qa` trigger QA environment deployment +- PRs to `main` trigger production deployment + +### Commit Message Format + +We follow [Conventional Commits](https://www.conventionalcommits.org/): + +``` +(): + + + +