diff --git a/.github/workflows/copilot-build-backend.yml b/.github/workflows/copilot-build-backend.yml index e82da790e..aa463eb0c 100644 --- a/.github/workflows/copilot-build-backend.yml +++ b/.github/workflows/copilot-build-backend.yml @@ -34,23 +34,35 @@ jobs: - uses: actions/checkout@v3 with: clean: true + fetch-depth: 0 - - name: Package Copilot Chat WebAPI - run: | - scripts\deploy\package-webapi.ps1 -Configuration Release -DotnetFramework net6.0 -TargetRuntime win-x64 -OutputDirectory ${{ github.workspace }}\scripts\deploy + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v0 + with: + versionSpec: '5.x' - - name: Check formatting of Copilot Chat WebAPI - run: | - cd webapi/ - dotnet format --verify-no-changes --verbosity diagnostic + - name: Determine version + id: gitversion + uses: gittools/actions/gitversion/execute@v0 - name: Set version tag id: versiontag run: | - $VERSION_TAG="$(Get-Date -Format "MMddHHmmss")" + $VERSION_TAG = "${{ steps.gitversion.outputs.Major }}." + $VERSION_TAG += "${{ steps.gitversion.outputs.Minor }}." + $VERSION_TAG += "${{ steps.gitversion.outputs.CommitsSinceVersionSource }}" echo $VERSION_TAG Write-Output "versiontag=$VERSION_TAG" >> $env:GITHUB_OUTPUT + - name: Package Copilot Chat WebAPI + run: | + scripts\deploy\package-webapi.ps1 -Configuration Release -DotnetFramework net6.0 -TargetRuntime win-x64 -OutputDirectory ${{ github.workspace }}\scripts\deploy -Version ${{ steps.versiontag.outputs.versiontag }} -InformationalVersion "Built from commit ${{ steps.gitversion.outputs.ShortSha }} on $(Get-Date -Format "yyyy-MM-dd")" + + - name: Check formatting of Copilot Chat WebAPI + run: | + cd webapi/ + dotnet format --verify-no-changes --verbosity diagnostic + - name: Upload package to artifacts uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/copilot-deploy-frontend.yml b/.github/workflows/copilot-deploy-frontend.yml index 77ee5fb9c..c32cdb5d4 100644 --- a/.github/workflows/copilot-deploy-frontend.yml +++ b/.github/workflows/copilot-deploy-frontend.yml @@ -38,6 +38,7 @@ jobs: - uses: actions/checkout@v3 with: clean: true + fetch-depth: 0 - name: Install Azure CLI run: | @@ -69,6 +70,24 @@ jobs: echo "::add-mask::$swaToken" echo "SWA_CLI_DEPLOYMENT_TOKEN=$swaToken" >> $GITHUB_ENV + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v0 + with: + versionSpec: '5.x' + + - name: Determine version + id: gitversion + uses: gittools/actions/gitversion/execute@v0 + + - name: Set version tag + id: versiontag + run: | + $VERSION_TAG = "${{ steps.gitversion.outputs.Major }}." + $VERSION_TAG += "${{ steps.gitversion.outputs.Minor }}." + $VERSION_TAG += "${{ steps.gitversion.outputs.CommitsSinceVersionSource }}" + echo $VERSION_TAG + Write-Output "versiontag=$VERSION_TAG" >> $env:GITHUB_OUTPUT + - name: Deploy SWA run: | - scripts/deploy/deploy-webapp.sh --subscription ${{secrets.AZURE_SUBSCRIPTION_ID}} --resource-group ${{vars.CC_DEPLOYMENT_GROUP_NAME}} --deployment-name ${{inputs.DEPLOYMENT_NAME}} --application-id ${{vars.APPLICATION_CLIENT_ID}} --authority ${{secrets.APPLICATION_AUTHORITY}} --no-redirect + scripts/deploy/deploy-webapp.sh --subscription ${{secrets.AZURE_SUBSCRIPTION_ID}} --resource-group ${{vars.CC_DEPLOYMENT_GROUP_NAME}} --deployment-name ${{inputs.DEPLOYMENT_NAME}} --application-id ${{vars.APPLICATION_CLIENT_ID}} --authority ${{secrets.APPLICATION_AUTHORITY}} --no-redirect -version ${{ steps.versiontag.outputs.versiontag }} -version-info "Built from commit ${{ steps.gitversion.outputs.ShortSha }} on $(Get-Date -Format "yyyy-MM-dd")" diff --git a/.github/workflows/update-version.sh b/.github/workflows/update-version.sh deleted file mode 100644 index 5db3622f6..000000000 --- a/.github/workflows/update-version.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/bin/bash - -POSITIONAL_ARGS=() - -while [[ $# -gt 0 ]]; do - case $1 in - -f|--file) - file="$2" - shift # past argument - shift # past value - ;; - -p|--propsFile) - propsFile="$2" - shift # past argument - shift # past value - ;; - -b|--buildAndRevisionNumber) - buildAndRevisionNumber="$2" - shift # past argument - shift # past value - ;; - -*|--*) - echo "Unknown option $1" - exit 1 - ;; - *) - POSITIONAL_ARGS+=("$1") # save positional arg - shift # past argument - ;; - esac -done - -set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters - -if [ -z "$file" ]; then - echo "ERROR: Parameter file (-f|--file) not provided" - exit 1; -elif [ ! -f "$file" ]; then - echo "ERROR: file ${file} not found" - exit 1; -fi - -if [ -n "$(cat $file | grep -i "false")" ]; then - echo "Project is marked as NOT packable - skipping." - exit 0; -fi - -if [ -z "$propsFile" ]; then - echo "ERROR: Parameter propsFile (-f|--file) not provided" - exit 1; -elif [ ! -f "$propsFile" ]; then - echo "ERROR: propsFile ${file} not found" - exit 1; -fi - -if [ -z "$buildAndRevisionNumber" ]; then - echo "ERROR: Parameter buildAndRevisionNumber (-b|--buildAndRevisionNumber) not provided" - exit 1; -fi - -propsVersionString=$(cat $propsFile | grep -i ""); -regex="([0-9.]*)<\/Version>" -if [[ $propsVersionString =~ $regex ]]; then - propsVersion=${BASH_REMATCH[1]} -else - echo "ERROR: Version tag not found in propsFile" - exit 1; -fi - -if [ -z "$propsVersion" ]; then - echo "ERROR: Version tag not found in propsFile" - exit 1; -elif [[ ! "$propsVersion" =~ ^0.* ]]; then - echo "ERROR: Version expected to start with 0. Actual: ${propsVersion}" - exit 1; -fi - -fullVersionString="${propsVersion}.${buildAndRevisionNumber}-preview" - -if [[ ! "$fullVersionString" =~ ^0.* ]]; then - echo "ERROR: Version expected to start with 0. Actual: ${fullVersionString}" - exit 1; -fi - -echo "==== Project: ${file} ===="; -echo "propsFile = ${propsFile}" -echo "buildAndRevisionNumber = ${buildAndRevisionNumber}" -echo "version prefix from propsFile = ${propsVersion}" -echo "full version string: ${fullVersionString}" - -versionInProj=$(cat $file | grep -i ""); -if [ -n "$versionInProj" ]; then - # Version tag already exists in the csproj. Let's replace it. - echo "Updating version tag..." - content=$(cat $file | sed --expression="s/\([0-9]*.[0-9]*\)<\/Version>/$fullVersionString<\/Version>/g"); -else - # Version tag not found in the csproj. Let's add it. - echo "Project is packable - adding version tag..." - content=$(cat $file | sed --expression="s/<\/Project>/$fullVersionString<\/Version><\/PropertyGroup><\/Project>/g"); -fi - -if [ $? -ne 0 ]; then exit 1; fi -echo "$content" && echo "$content" > $file; -if [ $? -ne 0 ]; then exit 1; fi - -echo "DONE"; -echo ""; diff --git a/.gitignore b/.gitignore index 719f5ba7d..99475c678 100644 --- a/.gitignore +++ b/.gitignore @@ -485,5 +485,8 @@ webapp/public/.well-known* # Auto-generated solution file from Visual Studio webapi/CopilotChatWebApi.sln +# Files created for deployments +/deploy/ + # Tesseract OCR language data files -*.traineddata \ No newline at end of file +*.traineddata diff --git a/scripts/deploy/deploy-webapp.ps1 b/scripts/deploy/deploy-webapp.ps1 index 88b3d1c99..0be6c23d8 100644 --- a/scripts/deploy/deploy-webapp.ps1 +++ b/scripts/deploy/deploy-webapp.ps1 @@ -27,7 +27,15 @@ param( [Parameter(Mandatory=$false)] [string] # Authority for client applications that are not configured as multi-tenant. - $Authority="https://login.microsoftonline.com/common" + $Authority="https://login.microsoftonline.com/common", + + [string] + # Version to display in UI. + $Version = "", + + [string] + # Additional information given in version info. (Ex: commit SHA) + $VersionInfo = "" ) Write-Host "Setting up Azure credentials..." @@ -64,6 +72,8 @@ Write-Host "Writing environment variables to '$envFilePath'..." "REACT_APP_AAD_AUTHORITY=$Authority" | Out-File -FilePath $envFilePath -Append "REACT_APP_AAD_CLIENT_ID=$ApplicationClientId" | Out-File -FilePath $envFilePath -Append "REACT_APP_SK_API_KEY=$webapiApiKey" | Out-File -FilePath $envFilePath -Append +"REACT_APP_SK_VERSION=$Version" | Out-File -FilePath $envFilePath -Append +"REACT_APP_SK_BUILD_INFO=$VersionInfo" | Out-File -FilePath $envFilePath -Append Write-Host "Generating SWA config..." $swaConfig = $(Get-Content "$PSScriptRoot/../../webapp/template.swa-cli.config.json" -Raw) diff --git a/scripts/deploy/deploy-webapp.sh b/scripts/deploy/deploy-webapp.sh index 66ef188cc..3414d7b3f 100755 --- a/scripts/deploy/deploy-webapp.sh +++ b/scripts/deploy/deploy-webapp.sh @@ -15,6 +15,8 @@ usage() { echo " -d, --deployment-name DEPLOYMENT_NAME Name of the deployment from a 'deploy-azure.sh' deployment (mandatory)" echo " -a, --application-id APPLICATION_ID Client application ID (mandatory)" echo " -au, --authority Authority to use for client applications that are not configured as multi-tenant. Defaults to (https://login.microsoftonline.com/common) if not specified." + echo " -v --version VERSION Version to display in UI (default: 1.0.0)" + echo " -i --version-info INFO Additional info to put in version details" echo " -nr, --no-redirect Do not attempt to register redirect URIs with the client application" } @@ -47,6 +49,16 @@ while [[ $# -gt 0 ]]; do shift shift ;; + -v|--version) + VERSION="$2" + shift + shift + ;; + -i|--version-nfo) + VERSION_INFO="$2" + shift + shift + ;; -nr|--no-redirect) NO_REDIRECT=true shift @@ -97,6 +109,8 @@ echo "REACT_APP_BACKEND_URI=https://$WEB_API_URL/" > $ENV_FILE_PATH echo "REACT_APP_AAD_AUTHORITY=$AUTHORITY" >> $ENV_FILE_PATH echo "REACT_APP_AAD_CLIENT_ID=$APPLICATION_ID" >> $ENV_FILE_PATH echo "REACT_APP_SK_API_KEY=$WEB_API_KEY" >> $ENV_FILE_PATH +echo "REACT_APP_SK_VERSION=$VERSION" >> $ENV_FILE_PATH +echo "REACT_APP_SK_BUILD_INFO=$VERSION_INFO" >> $ENV_FILE_PATH echo "Writing swa-cli.config.json..." SWA_CONFIG_FILE_PATH="$SCRIPT_ROOT/../../webapp/swa-cli.config.json" diff --git a/scripts/deploy/package-webapi.ps1 b/scripts/deploy/package-webapi.ps1 index 81ae21ccf..586570751 100755 --- a/scripts/deploy/package-webapi.ps1 +++ b/scripts/deploy/package-webapi.ps1 @@ -18,7 +18,15 @@ param( [string] # Output directory for published assets. - $OutputDirectory = "$PSScriptRoot" + $OutputDirectory = "$PSScriptRoot", + + [string] + # Version to give to assemblies and files. + $Version = "1.0.0", + + [string] + # Additional information given in version info. + $InformationalVersion = "" ) Write-Host "BuildConfiguration: $BuildConfiguration" @@ -37,7 +45,7 @@ if (!(Test-Path $publishOutputDirectory)) { } Write-Host "Build configuration: $BuildConfiguration" -dotnet publish "$PSScriptRoot/../../webapi/CopilotChatWebApi.csproj" --configuration $BuildConfiguration --framework $DotNetFramework --runtime $TargetRuntime --self-contained --output "$publishOutputDirectory" +dotnet publish "$PSScriptRoot/../../webapi/CopilotChatWebApi.csproj" --configuration $BuildConfiguration --framework $DotNetFramework --runtime $TargetRuntime --self-contained --output "$publishOutputDirectory" /p:AssemblyVersion=$Version /p:FileVersion=$Version /p:InformationalVersion=$InformationalVersion if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/scripts/deploy/package-webapi.sh b/scripts/deploy/package-webapi.sh index f4fe48f19..972ff86fd 100644 --- a/scripts/deploy/package-webapi.sh +++ b/scripts/deploy/package-webapi.sh @@ -14,7 +14,9 @@ usage() { echo " -c, --configuration CONFIGURATION Build configuration (default: Release)" echo " -d, --dotnet DOTNET_FRAMEWORK_VERSION Target dotnet framework (default: net6.0)" echo " -r, --runtime TARGET_RUNTIME Runtime identifier (default: linux-x64)" - echo " -p, --output OUTPUT_DIRECTORY Output directory (default: $SCRIPT_ROOT)" + echo " -o, --output OUTPUT_DIRECTORY Output directory (default: $SCRIPT_ROOT)" + echo " -v --version VERSION Version to set files to (default: 1.0.0)" + echo " -i --info INFO Additional info to put in version details" echo " -nz, --no-zip Do not zip package (default: false)" } @@ -42,6 +44,16 @@ while [[ $# -gt 0 ]]; do shift shift ;; + -v|--version) + VERSION="$2" + shift + shift + ;; + -i|--info) + INFO="$2" + shift + shift + ;; -nz|--no-zip) NO_ZIP=true shift @@ -58,6 +70,8 @@ done : "${CONFIGURATION:="Release"}" : "${DOTNET:="net6.0"}" : "${RUNTIME:="linux-x64"}" +: "${VERSION:="1.0.0"}" +: "${INFO:=""}" : "${OUTPUT_DIRECTORY:="$SCRIPT_ROOT"}" PUBLISH_OUTPUT_DIRECTORY="$OUTPUT_DIRECTORY/publish" @@ -72,7 +86,7 @@ if [[ ! -d "$PUBLISH_ZIP_DIRECTORY" ]]; then fi echo "Build configuration: $CONFIGURATION" -dotnet publish "$SCRIPT_ROOT/../../webapi/CopilotChatWebApi.csproj" --configuration $CONFIGURATION --framework $DOTNET --runtime $RUNTIME --self-contained --output "$PUBLISH_OUTPUT_DIRECTORY" +dotnet publish "$SCRIPT_ROOT/../../webapi/CopilotChatWebApi.csproj" --configuration $CONFIGURATION --framework $DOTNET --runtime $RUNTIME --self-contained --output "$PUBLISH_OUTPUT_DIRECTORY" /p:AssemblyVersion=$VERSION /p:FileVersion=$VERSION /p:InformationalVersion=$INFO if [ $? -ne 0 ]; then exit 1 fi diff --git a/webapi/Controllers/ServiceOptionsController.cs b/webapi/Controllers/ServiceOptionsController.cs index 28adf3758..e0dd66e5a 100644 --- a/webapi/Controllers/ServiceOptionsController.cs +++ b/webapi/Controllers/ServiceOptionsController.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Diagnostics; +using System.Reflection; using CopilotChat.WebApi.Models.Response; using CopilotChat.WebApi.Options; using Microsoft.AspNetCore.Authorization; @@ -39,15 +41,24 @@ public ServiceOptionsController( [ProducesResponseType(StatusCodes.Status200OK)] public IActionResult GetServiceOptions() { - return this.Ok( - new ServiceOptionsResponse() + var response = new ServiceOptionsResponse() + { + MemoryStore = new MemoryStoreOptionResponse() { - MemoryStore = new MemoryStoreOptionResponse() - { - Types = Enum.GetNames(typeof(MemoryStoreOptions.MemoryStoreType)), - SelectedType = this._memoryStoreOptions.Type.ToString() - } - } - ); + Types = Enum.GetNames(typeof(MemoryStoreOptions.MemoryStoreType)), + SelectedType = this._memoryStoreOptions.Type.ToString() + }, + Version = GetAssemblyFileVersion() + }; + + return this.Ok(response); + } + + private static string GetAssemblyFileVersion() + { + Assembly assembly = Assembly.GetExecutingAssembly(); + FileVersionInfo fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location); + + return fileVersion.FileVersion ?? string.Empty; } } diff --git a/webapi/Models/Response/ServiceOptionsResponse.cs b/webapi/Models/Response/ServiceOptionsResponse.cs index d5eda9cba..dad1cb091 100644 --- a/webapi/Models/Response/ServiceOptionsResponse.cs +++ b/webapi/Models/Response/ServiceOptionsResponse.cs @@ -13,6 +13,12 @@ public class ServiceOptionsResponse /// [JsonPropertyName("memoryStore")] public MemoryStoreOptionResponse MemoryStore { get; set; } = new MemoryStoreOptionResponse(); + + /// + /// Version of this application. + /// + [JsonPropertyName("version")] + public string Version { get; set; } = string.Empty; } /// diff --git a/webapp/src/components/header/settings-dialog/SettingsDialog.tsx b/webapp/src/components/header/settings-dialog/SettingsDialog.tsx index 9b82bcc32..f7c614ad5 100644 --- a/webapp/src/components/header/settings-dialog/SettingsDialog.tsx +++ b/webapp/src/components/header/settings-dialog/SettingsDialog.tsx @@ -56,7 +56,7 @@ interface ISettingsDialogProps { export const SettingsDialog: React.FC = ({ open, closeDialog }) => { const classes = useClasses(); const dialogClasses = useDialogClasses(); - const { settings, tokenUsage } = useAppSelector((state: RootState) => state.app); + const { serviceOptions, settings, tokenUsage } = useAppSelector((state: RootState) => state.app); return ( = ({ open, closeDial })} + + + +

About

+
+ + + Backend version: {serviceOptions.version} +
+ Frontend version: {process.env.REACT_APP_SK_VERSION ?? '-'} +
+ {process.env.REACT_APP_SK_BUILD_INFO} +
+
+
+ diff --git a/webapp/src/libs/models/ServiceOptions.ts b/webapp/src/libs/models/ServiceOptions.ts index f275ba5fb..b5335f6f3 100644 --- a/webapp/src/libs/models/ServiceOptions.ts +++ b/webapp/src/libs/models/ServiceOptions.ts @@ -7,4 +7,5 @@ export interface MemoryStore { export interface ServiceOptions { memoryStore: MemoryStore; + version: string; } diff --git a/webapp/src/redux/features/app/AppState.ts b/webapp/src/redux/features/app/AppState.ts index 1baa327ba..9634ba1d2 100644 --- a/webapp/src/redux/features/app/AppState.ts +++ b/webapp/src/redux/features/app/AppState.ts @@ -134,5 +134,5 @@ export const initialState: AppState = { tokenUsage: {}, features: Features, settings: Settings, - serviceOptions: { memoryStore: { types: [], selectedType: '' } }, + serviceOptions: { memoryStore: { types: [], selectedType: '' }, version: '' }, };