diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml index b4e41fc42..c91575804 100644 --- a/.github/workflows/stale-bot.yml +++ b/.github/workflows/stale-bot.yml @@ -1,19 +1,82 @@ -name: 'Stale Bot' +name: "Manage Stale Issues, PRs & Unmerged Branches" on: schedule: - - cron: '30 1 * * *' - + - cron: '30 1 * * *' # Runs daily at 1:30 AM UTC + workflow_dispatch: # Allows manual triggering permissions: contents: write issues: write pull-requests: write - jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9 + - name: Mark Stale Issues and PRs + uses: actions/stale@v9 with: - stale-issue-message: 'This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 30 days.' + stale-issue-message: "This issue is stale because it has been open 180 days with no activity. Remove stale label or comment, or it will be closed in 30 days." + stale-pr-message: "This PR is stale because it has been open 180 days with no activity. Please update or it will be closed in 30 days." days-before-stale: 180 days-before-close: 30 + exempt-issue-labels: "keep" + exempt-pr-labels: "keep" + cleanup-branches: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch full history for accurate branch checks + - name: Fetch All Branches + run: git fetch --all --prune + - name: List Merged Branches With No Activity in Last 3 Months + run: | + + echo "Branch Name,Last Commit Date,Committer,Committed In Branch,Action" > merged_branches_report.csv + + for branch in $(git for-each-ref --format '%(refname:short) %(committerdate:unix)' refs/remotes/origin | awk -v date=$(date -d '3 months ago' +%s) '$2 < date {print $1}'); do + if [[ "$branch" != "origin/main" && "$branch" != "origin/dev" ]]; then + branch_name=${branch#origin/} + # Ensure the branch exists locally before getting last commit date + git fetch origin "$branch_name" || echo "Could not fetch branch: $branch_name" + last_commit_date=$(git log -1 --format=%ci "origin/$branch_name" || echo "Unknown") + committer_name=$(git log -1 --format=%cn "origin/$branch_name" || echo "Unknown") + committed_in_branch=$(git branch -r --contains "origin/$branch_name" | tr -d ' ' | paste -sd "," -) + echo "$branch_name,$last_commit_date,$committer_name,$committed_in_branch,Delete" >> merged_branches_report.csv + fi + done + - name: List PR Approved and Merged Branches Older Than 30 Days + run: | + + for branch in $(gh api repos/${{ github.repository }}/pulls --jq '.[] | select(.merged_at != null and (.base.ref == "main" or .base.ref == "dev")) | select(.merged_at | fromdateiso8601 < (now - 2592000)) | .head.ref'); do + # Ensure the branch exists locally before getting last commit date + git fetch origin "$branch" || echo "Could not fetch branch: $branch" + last_commit_date=$(git log -1 --format=%ci origin/$branch || echo "Unknown") + committer_name=$(git log -1 --format=%cn origin/$branch || echo "Unknown") + committed_in_branch=$(git branch -r --contains "origin/$branch" | tr -d ' ' | paste -sd "," -) + echo "$branch,$last_commit_date,$committer_name,$committed_in_branch,Delete" >> merged_branches_report.csv + done + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: List Open PR Branches With No Activity in Last 3 Months + run: | + + for branch in $(gh api repos/${{ github.repository }}/pulls --state open --jq '.[] | select(.base.ref == "main" or .base.ref == "dev") | .head.ref'); do + # Ensure the branch exists locally before getting last commit date + git fetch origin "$branch" || echo "Could not fetch branch: $branch" + last_commit_date=$(git log -1 --format=%ci origin/$branch || echo "Unknown") + committer_name=$(git log -1 --format=%cn origin/$branch || echo "Unknown") + if [[ $(date -d "$last_commit_date" +%s) -lt $(date -d '3 months ago' +%s) ]]; then + # If no commit in the last 3 months, mark for deletion + committed_in_branch=$(git branch -r --contains "origin/$branch" | tr -d ' ' | paste -sd "," -) + echo "$branch,$last_commit_date,$committer_name,$committed_in_branch,Delete" >> merged_branches_report.csv + fi + done + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload CSV Report of Inactive Branches + uses: actions/upload-artifact@v4 + with: + name: merged-branches-report + path: merged_branches_report.csv + retention-days: 30 diff --git a/code/backend/batch/utilities/helpers/env_helper.py b/code/backend/batch/utilities/helpers/env_helper.py index 6163d19d9..31bd07354 100644 --- a/code/backend/batch/utilities/helpers/env_helper.py +++ b/code/backend/batch/utilities/helpers/env_helper.py @@ -362,6 +362,9 @@ def __load_config(self, **kwargs) -> None: self.SEMENTIC_KERNEL_SYSTEM_PROMPT = os.getenv( "SEMENTIC_KERNEL_SYSTEM_PROMPT", "" ) + + self.ENFORCE_AUTH = self.get_env_var_bool("ENFORCE_AUTH", "True") + logger.info("Initializing EnvHelper completed") def is_chat_model(self): diff --git a/code/create_app.py b/code/create_app.py index c272387f7..c32da6cec 100644 --- a/code/create_app.py +++ b/code/create_app.py @@ -541,5 +541,10 @@ def assistanttype(): result = ConfigHelper.get_active_config_or_default() return jsonify({"ai_assistant_type": result.prompts.ai_assistant_type}) + @app.route("/api/checkauth", methods=["GET"]) + async def check_auth_enforced(): + """Check if the authentiction is enforced.""" + return jsonify({"is_auth_enforced": env_helper.ENFORCE_AUTH}) + app.register_blueprint(bp_chat_history_response, url_prefix="/api") return app diff --git a/code/frontend/src/api/api.ts b/code/frontend/src/api/api.ts index cc08a0c03..2f261db42 100644 --- a/code/frontend/src/api/api.ts +++ b/code/frontend/src/api/api.ts @@ -54,6 +54,25 @@ export async function getUserInfo(): Promise { } } +export async function checkAuthEnforced(): Promise { + try { + const response = await fetch("/api/checkauth", { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + if (!response.ok) { + throw new Error("Network response was not ok"); + } + const config = await response.json(); // Parse JSON response + return config.is_auth_enforced; + } catch (error) { + console.error("Failed to fetch configuration:", error); + return true; // Return true because we need to enforce auth by default + } +} + export async function getAssistantTypeApi() { try { const response = await fetch("/api/assistanttype", { diff --git a/code/frontend/src/pages/layout/Layout.test.tsx b/code/frontend/src/pages/layout/Layout.test.tsx index 5e22bc534..5f7d66b45 100644 --- a/code/frontend/src/pages/layout/Layout.test.tsx +++ b/code/frontend/src/pages/layout/Layout.test.tsx @@ -8,7 +8,7 @@ import { import Layout from "./Layout"; import { BrowserRouter } from "react-router-dom"; -import { getUserInfo } from "../../api/api"; +import { getUserInfo, checkAuthEnforced } from "../../api/api"; import { before } from "lodash"; import { hostname } from "os"; @@ -29,7 +29,7 @@ const DefaultLayoutPropsloderfalse = { }; jest.mock('../../api/api', () => ({ - getUserInfo: jest.fn() + getUserInfo: jest.fn(), checkAuthEnforced: jest.fn() })); @@ -72,6 +72,7 @@ describe("Layout Component", () => { }, }); ;(getUserInfo as jest.Mock).mockResolvedValue(mocklist) + ;(checkAuthEnforced as jest.Mock).mockResolvedValue(true) await act(async () => { render( diff --git a/code/frontend/src/pages/layout/Layout.tsx b/code/frontend/src/pages/layout/Layout.tsx index 21c68b621..7ed24fcfa 100644 --- a/code/frontend/src/pages/layout/Layout.tsx +++ b/code/frontend/src/pages/layout/Layout.tsx @@ -9,7 +9,7 @@ import { import { Dialog, Stack, TextField } from "@fluentui/react"; import { ReactNode, useEffect, useRef, useState } from "react"; import { HistoryButton } from "../../components/HistoryButton/HistoryButton"; -import { getUserInfo } from "../../api"; +import { getUserInfo, checkAuthEnforced } from "../../api"; import SpinnerComponent from '../../components/Spinner/Spinner'; @@ -52,6 +52,12 @@ const Layout = ({ children,toggleSpinner, ...props }: LayoutProps) => { const firstRender = useRef(true); const getUserInfoList = async () => { + const isAuthEnforced = await checkAuthEnforced(); // Check if auth is enforced + if(!isAuthEnforced) { + setShowAuthMessage(false); + return; + } + const userInfoList = await getUserInfo(); if ( userInfoList.length === 0 && diff --git a/docs/container_registry_migration.md b/docs/container_registry_migration.md new file mode 100644 index 000000000..2b41eafee --- /dev/null +++ b/docs/container_registry_migration.md @@ -0,0 +1,146 @@ +# Guide: Migrating Azure Web App Service to a New Container Registry + +## Overview + +### Current Problem: +- The **CWYD Container Image** is being published in the **External ACR** (Azure Container Registry). + +### Goal: +- The goal is to **migrate container images** from various applications to a common **CSA CTO Production Azure Container Registry**, ensuring all the different images are consolidated in one centralized location. + +--- + +## Step-by-Step Guide: Migrating Azure Web App Service to a New Container Registry + +This guide will help you seamlessly switch the container registry for your **Azure Web App Service** from Azure Container Registry (ACR) to the new registry **`cwydcontainerreg`**. + +Follow the steps below to ensure a smooth migration. + +### Prerequisites: +Before you begin, ensure you have the following: +- Access to the **Azure Portal**. +- The **container image** in the new registry is ready and accessible. + +--- + +### Step 1: Obtain Details for the New Registry + +Before you begin, ensure you have the following information: +- **Registry URL**: The URL of the new registry (`https://cwydcontainerreg.azurecr.io`). +- **Image Name and Tag**: The full name and tag of the image you want to use: + - **Web App Image**: `rag-webapp:latest` + - **Admin Web App Image**: `rag-adminwebapp:latest` + - **Function App Image**: `rag-backend:latest` + +--- + +### Step 2: Update Azure Web App Service Configuration Using Azure Portal + +1. **Log in to Azure Portal**: + - Open [Azure Portal](https://portal.azure.com/). + +2. **Locate Your Resource Group and Web App Service**: + - Navigate to resource group which you have created for CWYD. + - Navigate to **Web App Service**: From the list of resources, find and select **App Service** + +3. **Go to the Deployment Center**: + - In the left-hand menu, click on **Deployment**. + + ![Resource Menu](images/resource_menu.png) + + +4. **Update Image Source**: + - Change the **Registry Source** to **Private**. + - Set the **Server URL** to the new container registry (`https://cwydcontainerreg.azurecr.io`), as shown in the screenshot below. + - Set the **Full Image name** to the relevant image name and tag: + - For Web App: `rag-webapp:latest` + + ![Deployment Center](images/deployment_center.png) + +5. **Save Changes**: + - Click **Save** to save the configuration. + +--- + +### Step 3: Restart the Web App Service + +After updating the configuration, restart your **Web App Service** to apply the changes: + +1. In the **Web App Service overview page**, click on **Restart**. +2. Confirm the restart operation. + +--- + +### Step 4: Update Azure Admin Web App Service Configuration Using Azure Portal + +1. **Locate Your Resource Group and Admin Web App Service**: + - Navigate to resource group which you have created for CWYD. + - Navigate to **Admin App Service**: From the list of resources, find and select **App Service** that contains `admin` in its name + +2. **Go to the Deployment Center**: + - In the left-hand menu, click on **Deployment**. + +3. **Update Image Source for Admin Web App**: + - Change the **Registry Source** to **Private**. + - Set the **Server URL** to the new container registry (`https://cwydcontainerreg.azurecr.io`). + - Set the **Full Image name** to the relevant image name and tag: + - For **Admin Web App**: `rag-adminwebapp:latest` + +4. **Save Changes**: + - Click **Save** to save the configuration. + +--- + +### Step 5: Restart the Admin Web App Service + +After updating the configuration, restart your **Admin Web App Service** to apply the changes: + +1. In the **Admin Web App Service overview page**, click on **Restart**. +2. Confirm the restart operation. + +--- + +### Step 6: Update Azure Function App Service Configuration Using Azure Portal + +1. **Locate Your Resource Group and Function App Service**: + - Navigate to resource group which you have created for CWYD. + - Navigate to **Function App**: From the list of resources, find and select **Function App** + +2. **Go to the Deployment Center**: + - In the left-hand menu, click on **Deployment**. + +3. **Update Image Source for Function App**: + - Change the **Registry Source** to **Private**. + - Set the **Server URL** to the new container registry (`https://cwydcontainerreg.azurecr.io`). + - Set the **Full Image name** to the relevant image name and tag: + - For **Function App**: `rag-backend:latest` + +4. **Save Changes**: + - Click **Save** to save the configuration. + +--- + +### Step 7: Restart the Function App Service + +After updating the configuration, restart your **Function App Service** to apply the changes: + +1. In the **Function App Service overview page**, click on **Restart**. +2. Confirm the restart operation. + +--- + +### Step 8: Validate the Deployment + +1. **Access Your Web App**: + - Open the **Web App URL** in a browser to ensure it’s running correctly. + +2. **Access Your Admin Web App**: + - Open the **Admin Web App URL** in a browser to ensure it’s running correctly. + +--- + +By following these steps, your **Azure Web App Service** will now use the new container from the **CWYD registry**. + +For further assistance, feel free to reach out to your support team or log an issue on GitHub. + +--- diff --git a/docs/images/deployment_center.png b/docs/images/deployment_center.png new file mode 100644 index 000000000..834f6091d Binary files /dev/null and b/docs/images/deployment_center.png differ diff --git a/docs/images/resource_menu.png b/docs/images/resource_menu.png new file mode 100644 index 000000000..8d59d533f Binary files /dev/null and b/docs/images/resource_menu.png differ diff --git a/docs/model_configuration.md b/docs/model_configuration.md index f94dd1c65..aa988c649 100644 --- a/docs/model_configuration.md +++ b/docs/model_configuration.md @@ -29,7 +29,7 @@ This document outlines the necessary steps and configurations required for setti - `AZURE_OPENAI_VISION_MODEL_NAME`: The Azure OpenAI Model Name - example: `gpt-4` - `AZURE_OPENAI_VISION_MODEL_VERSION`: The Azure OpenAI Model Version - - example: `vision-preview` + - example: `turbo-2024-04-09` - `AZURE_OPENAI_VISION_MODEL_CAPACITY`: The Tokens per Minute Rate Limit (thousands) - example: `10` diff --git a/infra/main.bicep b/infra/main.bicep index cb981599f..69636c1ac 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -149,7 +149,7 @@ param azureOpenAIVisionModel string = 'gpt-4' param azureOpenAIVisionModelName string = 'gpt-4' @description('Azure OpenAI Vision Model Version') -param azureOpenAIVisionModelVersion string = 'vision-preview' +param azureOpenAIVisionModelVersion string = 'turbo-2024-04-09' @description('Azure OpenAI Vision Model Capacity - See here for more info https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/quota') param azureOpenAIVisionModelCapacity int = 10 diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 907fe272a..392fa45fc 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -40,7 +40,7 @@ param useAdvancedImageProcessing = bool(readEnvironmentVariable('USE_ADVANCED_IM param advancedImageProcessingMaxImages = int(readEnvironmentVariable('ADVANCED_IMAGE_PROCESSING_MAX_IMAGES', '1')) param azureOpenAIVisionModel = readEnvironmentVariable('AZURE_OPENAI_VISION_MODEL', 'gpt-4') param azureOpenAIVisionModelName = readEnvironmentVariable('AZURE_OPENAI_VISION_MODEL_NAME', 'gpt-4') -param azureOpenAIVisionModelVersion = readEnvironmentVariable('AZURE_OPENAI_VISION_MODEL_VERSION', 'vision-preview') +param azureOpenAIVisionModelVersion = readEnvironmentVariable('AZURE_OPENAI_VISION_MODEL_VERSION', 'turbo-2024-04-09') param azureOpenAIVisionModelCapacity = int(readEnvironmentVariable('AZURE_OPENAI_VISION_MODEL_CAPACITY', '10')) param azureOpenAIEmbeddingModel = readEnvironmentVariable('AZURE_OPENAI_EMBEDDING_MODEL', 'text-embedding-ada-002') param azureOpenAIEmbeddingModelName = readEnvironmentVariable('AZURE_OPENAI_EMBEDDING_MODEL_NAME', 'text-embedding-ada-002') diff --git a/infra/main.json b/infra/main.json index ab5eb1318..3150949ad 100644 --- a/infra/main.json +++ b/infra/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.33.93.31351", - "templateHash": "4812595955475306561" + "templateHash": "5128832335196651060" } }, "parameters": { @@ -315,7 +315,7 @@ }, "azureOpenAIVisionModelVersion": { "type": "string", - "defaultValue": "vision-preview", + "defaultValue": "turbo-2024-04-09", "metadata": { "description": "Azure OpenAI Vision Model Version" }