diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a110c018ec0a77c59f67cd7ec152e4ce72b646ac
--- /dev/null
+++ b/.github/workflows/build.yaml
@@ -0,0 +1,93 @@
+name: Build
+
+on:
+  workflow_dispatch:
+  pull_request:
+  push:
+    branches: [main, contribute]
+    tags: ["*"]
+
+jobs:
+  build:
+    strategy:
+      matrix:
+        runner: [windows-large]
+        configuration: [ReleaseOS]
+        addrsize: [64]
+        include:
+          - runner: windows-large
+            configuration: ReleaseOS
+            addrsize: 32
+    runs-on: ${{ matrix.runner }}
+    env:
+      AUTOBUILD_CONFIGURATION: ${{ matrix.configuration }}
+      AUTOBUILD_ADDRSIZE: ${{ matrix.addrsize }}
+      AUTOBUILD_INSTALLABLE_CACHE: ${{ github.workspace }}/.autobuild-installables
+      AUTOBUILD_VARIABLES_FILE: ${{ github.workspace }}/.build-variables/variables
+      AUTOBUILD_VSVER: "170" # vs2k22
+      LOGFAIL: debug # Show details when tests fail
+      GIT_REF: ${{ github.head_ref || github.ref }}
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v3
+        with:
+          ref: ${{ github.event.pull_request.head.sha || github.sha }}
+
+      - name: Checkout build variables
+        uses: actions/checkout@v3
+        with:
+          repository: secondlife/build-variables
+          ref: viewer
+          path: .build-variables
+
+      - name: Install autobuild and python dependencies
+        run: pip3 install autobuild llbase
+
+      - name: Cache autobuild packages
+        uses: actions/cache@v3
+        id: cache-installables
+        with:
+          path: .autobuild-installables
+          key: ${{ runner.os }}-${{ matrix.addrsize }}-${{ matrix.configuration }}-${{ hashFiles('autobuild.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-${{ matrix.addrsize }}-${{ matrix.configuration }}-
+            ${{ runner.os }}-${{ matrix.addrsize }}-
+
+      - name: Install windows dependencies
+        if: runner.os == 'Windows'
+        run: choco install nsis-unicode
+
+      - name: Build
+        id: build
+        shell: bash
+        env:
+          RUNNER_OS: ${{ runner.os }}
+        run: |
+          # On windows we need to point the build to the correct python
+          # as neither CMake's FindPython nor our custom Python.cmake module
+          # will resolve the correct interpreter location.
+          if [[ "$RUNNER_OS" == "Windows" ]]; then
+            export PYTHON="$(cygpath -m "$(which python)")"
+            echo "Python location: $PYTHON"
+          fi
+          
+          autobuild configure -- -DVIEWER_CHANNEL="Second Life Test ${GIT_REF##*/}"
+          autobuild  build --no-configure
+
+          # Find artifacts
+          if [[ "$RUNNER_OS" == "Windows" ]]; then
+            installer_path=$(find ./build-*/newview/ | grep '_Setup\.exe')
+            installer_name="$(basename $installer_path)"
+          elif [[ "$RUNNER_OS" == "macOS" ]]; then
+            installer_path=$(find ./build-*/newview/ | grep '\.dmg')
+            installer_name="$(basename $installer_path)"
+          fi
+
+          echo "installer_path=$installer_path" >> $GITHUB_OUTPUT
+          echo "installer_name=$installer_name" >> $GITHUB_OUTPUT
+      
+      - name: Upload installer
+        uses: actions/upload-artifact@v3
+        with:
+          name: ${{ steps.build.outputs.installer_name }}
+          path: ${{ steps.build.outputs.installer_path }}
diff --git a/autobuild.xml b/autobuild.xml
index 40a40bba3cefb616462c98f9ea2b8891fc036c82..9118035a43c929c3e02c619a282f86ada4033a15 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -3403,7 +3403,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>/t:Build</string>
                   <string>/p:useenv=true</string>
                   <string>/verbosity:minimal</string>
-                  <string>/toolsversion:4.0</string>
                   <string>/p:VCBuildAdditionalOptions= /incremental</string>
                 </array>
               </map>
@@ -3477,7 +3476,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
                   <string>/t:Build</string>
                   <string>/p:useenv=true</string>
                   <string>/verbosity:minimal</string>
-                  <string>/toolsversion:4.0</string>
                   <string>/p:VCBuildAdditionalOptions= /incremental</string>
                 </array>
               </map>
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
index 9535e62e8fbb19ad4a654279c5bc28abcfd7232d..58a64a87551569bff85d1288e51a5280055fbf98 100644
--- a/indra/cmake/00-Common.cmake
+++ b/indra/cmake/00-Common.cmake
@@ -61,7 +61,7 @@ if (WINDOWS)
   # CP changed to only append the flag for 32bit builds - on 64bit builds,
   # locally at least, the build output is spammed with 1000s of 'D9002'
   # warnings about this switch being ignored.
-  if( ADDRESS_SIZE EQUAL 32 )
+  if(ADDRESS_SIZE EQUAL 32 AND DEFINED ENV{"TEAMCITY_PROJECT_NAME"})
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /p:PreferredToolArchitecture=x64")  
   endif()
   # zlib has assembly-language object files incompatible with SAFESEH
diff --git a/indra/cmake/Python.cmake b/indra/cmake/Python.cmake
index dbf5033ce574fef8f25c457ab5b64ac62f917d66..f9259f6c2b7654a06a4c3943d11562db841998ab 100644
--- a/indra/cmake/Python.cmake
+++ b/indra/cmake/Python.cmake
@@ -2,7 +2,11 @@
 
 set(PYTHONINTERP_FOUND)
 
-if (WINDOWS)
+if (DEFINED ENV{PYTHON})
+  # Allow python executable to be explicitly set
+  set(python "$ENV{PYTHON}")
+  set(PYTHONINTERP_FOUND ON)
+elseif (WINDOWS)
   # On Windows, explicitly avoid Cygwin Python.
 
   # if the user has their own version of Python installed, prefer that
@@ -43,7 +47,7 @@ else()
   if (python)
     set(PYTHONINTERP_FOUND ON)
   endif (python)
-endif (WINDOWS)
+endif (DEFINED ENV{PYTHON})
 
 if (NOT python)
   message(FATAL_ERROR "No Python interpreter found")
diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi
index 60e26274cbe87226f2d041e62d32304f18840f0d..778d32e66c0b51a9f29d5feff26e24bfcf3c5b1e 100644
--- a/indra/newview/installers/windows/installer_template.nsi
+++ b/indra/newview/installers/windows/installer_template.nsi
@@ -524,6 +524,7 @@ FunctionEnd
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Make sure the user can uninstall
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+/* Unused
 Function un.CheckIfAdministrator
     DetailPrint $(CheckAdministratorUnInstDP)
     UserInfo::GetAccountType
@@ -535,6 +536,7 @@ lbl_is_admin:
     Return
 
 FunctionEnd
+*/
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Function CheckWillUninstallV2               
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 89481add2942377b764a9e90a05036691ba2604b..c312c1d248a1c4f9bee72b7b6e2ed7e81d826f32 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -790,27 +790,15 @@ def package_finish(self):
             
         # Check two paths, one for Program Files, and one for Program Files (x86).
         # Yay 64bit windows.
-        for ProgramFiles in 'ProgramFiles', 'ProgramFiles(x86)':
-            NSIS_path = os.path.expandvars(r'${%s}\NSIS\makensis.exe' % ProgramFiles)
-            if os.path.exists(NSIS_path):
-                break
-        installer_created=False
-        nsis_attempts=3
-        nsis_retry_wait=15
-        for attempt in range(nsis_attempts):
-            try:
-                self.run_command([NSIS_path, '/V2', self.dst_path_of(tempfile)])
-            except ManifestError as err:
-                if attempt+1 < nsis_attempts:
-                    print("nsis failed, waiting %d seconds before retrying" % nsis_retry_wait, file=sys.stderr)
-                    time.sleep(nsis_retry_wait)
-                    nsis_retry_wait*=2
-            else:
-                # NSIS worked! Done!
-                break
-        else:
-            print("Maximum nsis attempts exceeded; giving up", file=sys.stderr)
-            raise
+        nsis_path = "makensis.exe"
+        for program_files in '${programfiles}', '${programfiles(x86)}':
+            for nesis_path in 'NSIS', 'NSIS\\Unicode':
+                possible_path = os.path.expandvars(f"{program_files}\\{nesis_path}\\makensis.exe")
+                if os.path.exists(possible_path):
+                    nsis_path = possible_path
+                    break
+
+        self.run_command([possible_path, '/V2', self.dst_path_of(tempfile)])
 
         self.sign(installer_file)
         self.created_path(self.dst_path_of(installer_file))