Merge branch 'project/one-click' of https://github.com/secondlife/viewer
# Conflicts: # indra/cmake/Velopack.cmake # indra/newview/CMakeLists.txt # indra/newview/app_settings/settings.xml # indra/newview/installers/windows/installer_template.nsi # indra/newview/llappviewer.cppmaster
commit
f95833ee6a
|
|
@ -92,8 +92,8 @@ jobs:
|
|||
# Only set variants to the one configuration: don't let build.sh loop
|
||||
# over variants, let GitHub distribute variants over multiple hosts.
|
||||
variants: ${{ matrix.configuration }}
|
||||
# Pass USE_VELOPACK to CMake when using Velopack installer (default) - Windows only
|
||||
autobuild_configure_parameters: ${{ contains(matrix.runner, 'windows') && (github.event.inputs.installer_type || 'velopack') == 'velopack' && '-- -DUSE_VELOPACK:BOOL=ON' || '' }}
|
||||
# Pass USE_VELOPACK to CMake when using Velopack installer (default) - Windows and macOS
|
||||
autobuild_configure_parameters: ${{ (contains(matrix.runner, 'windows') || contains(matrix.runner, 'macos')) && (github.event.inputs.installer_type || 'velopack') == 'velopack' && '-- -DUSE_VELOPACK:BOOL=ON' || '' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
|
@ -136,8 +136,14 @@ jobs:
|
|||
with:
|
||||
token: ${{ github.token }}
|
||||
|
||||
- name: Setup .NET for Velopack
|
||||
if: (runner.os == 'Windows' || runner.os == 'macOS') && (github.event.inputs.installer_type || 'velopack') == 'velopack'
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '9.0.x'
|
||||
|
||||
- name: Install Velopack CLI
|
||||
if: runner.os == 'Windows' && (github.event.inputs.installer_type || 'velopack') == 'velopack'
|
||||
if: (runner.os == 'Windows' || runner.os == 'macOS') && (github.event.inputs.installer_type || 'velopack') == 'velopack'
|
||||
shell: bash
|
||||
run: dotnet tool install -g vpk
|
||||
|
||||
|
|
@ -463,6 +469,10 @@ jobs:
|
|||
with:
|
||||
pattern: "*-metadata"
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: "*-releases"
|
||||
|
||||
- name: Rename metadata
|
||||
run: |
|
||||
cp Windows-metadata/autobuild-package.xml Windows-autobuild-package.xml
|
||||
|
|
@ -488,12 +498,14 @@ jobs:
|
|||
generate_release_notes: true
|
||||
target_commitish: ${{ github.sha }}
|
||||
append_body: true
|
||||
fail_on_unmatched_files: true
|
||||
fail_on_unmatched_files: false
|
||||
files: |
|
||||
macOS-installer/*.dmg
|
||||
Windows-installer/*.exe
|
||||
*-autobuild-package.xml
|
||||
*-viewer_version.txt
|
||||
Windows-releases/*
|
||||
macOS-releases/*
|
||||
|
||||
- name: post release URL
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -3190,11 +3190,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
|||
<key>creds</key>
|
||||
<string>github</string>
|
||||
<key>hash</key>
|
||||
<string>77d82909367f116c60edd073f584d0c53e6ea591</string>
|
||||
<string>81a97ec4fc491011726097e6dc8359f537fa7935</string>
|
||||
<key>hash_algorithm</key>
|
||||
<string>sha1</string>
|
||||
<key>url</key>
|
||||
<string>https://api.github.com/repos/secondlife-3p/3p-velopack/releases/assets/330718186</string>
|
||||
<string>https://api.github.com/repos/secondlife-3p/3p-velopack/releases/assets/343914203</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>windows64</string>
|
||||
|
|
@ -3206,11 +3206,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
|||
<key>creds</key>
|
||||
<string>github</string>
|
||||
<key>hash</key>
|
||||
<string>3cabca6840973bbb3a90bf0cc3390bc86e745379</string>
|
||||
<string>17b1373f15b33cd86ca30743a383bf4de63d5c09</string>
|
||||
<key>hash_algorithm</key>
|
||||
<string>sha1</string>
|
||||
<key>url</key>
|
||||
<string>https://api.github.com/repos/secondlife-3p/3p-velopack/releases/assets/330718181</string>
|
||||
<string>https://api.github.com/repos/secondlife-3p/3p-velopack/releases/assets/343914198</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin64</string>
|
||||
|
|
@ -3223,7 +3223,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
|||
<key>copyright</key>
|
||||
<string>Velopack Ltd.</string>
|
||||
<key>version</key>
|
||||
<string>a2592d1.20376699173</string>
|
||||
<string>a2592d1.21222737470</string>
|
||||
<key>name</key>
|
||||
<string>velopack</string>
|
||||
<key>description</key>
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ set(cmake_SOURCE_FILES
|
|||
UI.cmake
|
||||
UnixInstall.cmake
|
||||
Variables.cmake
|
||||
Velopack.cmake
|
||||
VHACD.cmake
|
||||
ViewerMiscLibs.cmake
|
||||
VisualLeakDetector.cmake
|
||||
|
|
|
|||
|
|
@ -4,12 +4,9 @@
|
|||
|
||||
include_guard()
|
||||
|
||||
# USE_VELOPACK controls whether to use Velopack for Windows installer packaging (instead of NSIS)
|
||||
# This is separate from the ll::velopack library which is always linked on Windows
|
||||
option(USE_VELOPACK "Use Velopack for Windows installer packaging" OFF)
|
||||
# USE_VELOPACK controls whether to use Velopack for installer packaging (instead of NSIS/DMG)
|
||||
option(USE_VELOPACK "Use Velopack for installer packaging" OFF)
|
||||
|
||||
# TODO: Add Mac and Linux support
|
||||
# Only available on Windows (so far)
|
||||
if (USE_VELOPACK)
|
||||
if (WINDOWS)
|
||||
include(Prebuilt)
|
||||
|
|
@ -40,4 +37,34 @@ if (USE_VELOPACK)
|
|||
|
||||
target_compile_definitions(ll::velopack INTERFACE LL_VELOPACK=1)
|
||||
endif()
|
||||
|
||||
elseif (DARWIN)
|
||||
include(Prebuilt)
|
||||
use_prebuilt_binary(velopack)
|
||||
|
||||
add_library(ll::velopack INTERFACE IMPORTED)
|
||||
|
||||
target_include_directories(ll::velopack SYSTEM INTERFACE
|
||||
${LIBS_PREBUILT_DIR}/include/velopack
|
||||
)
|
||||
|
||||
target_link_libraries(ll::velopack INTERFACE
|
||||
${ARCH_PREBUILT_DIRS_RELEASE}/libvelopack_libc.a
|
||||
)
|
||||
|
||||
# macOS system frameworks required by Velopack (Rust static library dependencies)
|
||||
target_link_libraries(ll::velopack INTERFACE
|
||||
"-framework Foundation"
|
||||
"-framework Security"
|
||||
"-framework SystemConfiguration"
|
||||
"-framework AppKit"
|
||||
"-framework CoreFoundation"
|
||||
"-framework CoreServices"
|
||||
"-framework IOKit"
|
||||
"-liconv"
|
||||
"-lresolv"
|
||||
)
|
||||
|
||||
target_compile_definitions(ll::velopack INTERFACE LL_VELOPACK=1)
|
||||
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -787,6 +787,7 @@ set(viewer_SOURCE_FILES
|
|||
llurllineeditorctrl.cpp
|
||||
llurlwhitelist.cpp
|
||||
llversioninfo.cpp
|
||||
llvvmquery.cpp
|
||||
llviewchildren.cpp
|
||||
llviewerassetstats.cpp
|
||||
llviewerassetstorage.cpp
|
||||
|
|
@ -1639,6 +1640,7 @@ set(viewer_HEADER_FILES
|
|||
llurllineeditorctrl.h
|
||||
llurlwhitelist.h
|
||||
llversioninfo.h
|
||||
llvvmquery.h
|
||||
llviewchildren.h
|
||||
llviewerassetstats.h
|
||||
llviewerassetstorage.h
|
||||
|
|
@ -1845,6 +1847,8 @@ if (DARWIN)
|
|||
LIST(APPEND viewer_SOURCE_FILES llappviewermacosx-objc.h)
|
||||
LIST(APPEND viewer_SOURCE_FILES llfilepicker_mac.mm)
|
||||
LIST(APPEND viewer_HEADER_FILES llfilepicker_mac.h)
|
||||
LIST(APPEND viewer_SOURCE_FILES llvelopack.cpp)
|
||||
LIST(APPEND viewer_HEADER_FILES llvelopack.h)
|
||||
|
||||
set_source_files_properties(
|
||||
llappviewermacosx-objc.mm
|
||||
|
|
@ -2878,6 +2882,7 @@ if (DARWIN)
|
|||
--arch=${ARCH}
|
||||
--artwork=${ARTWORK_DIR}
|
||||
"--bugsplat=${BUGSPLAT_DB}"
|
||||
--bundleid=${MACOSX_BUNDLE_GUI_IDENTIFIER}
|
||||
"--discord=${USE_DISCORD}"
|
||||
"--fmodstudio=${USE_FMODSTUDIO}"
|
||||
"--openal=${USE_OPENAL}"
|
||||
|
|
|
|||
|
|
@ -6899,7 +6899,17 @@
|
|||
<key>Backup</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
|
||||
<key>LastInstallVersion</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Version number of last instance of the viewer that you installed</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>String</string>
|
||||
<key>Value</key>
|
||||
<string></string>
|
||||
</map>
|
||||
<key>LastSnapshotToProfileHeight</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ void LLGridManager::initGrids()
|
|||
}
|
||||
|
||||
// <FS:ND> FIRE-20112 in case grid was set bt settings/cmd line, set it now
|
||||
if (mStartupGrid.size())
|
||||
if (!mStartupGrid.empty())
|
||||
setGridChoice(mStartupGrid);
|
||||
mStartupGrid = "";
|
||||
// </FS:ND>
|
||||
|
|
@ -1340,7 +1340,7 @@ std::string LLGridManager::getSLURLBase(const std::string& grid)
|
|||
// deal with hand edited entries
|
||||
std::string grid_norm = grid;
|
||||
|
||||
if (grid_norm.size())
|
||||
if (!grid_norm.empty())
|
||||
{
|
||||
size_t pos = grid_norm.find_last_of("/");
|
||||
|
||||
|
|
@ -1356,6 +1356,30 @@ std::string LLGridManager::getSLURLBase(const std::string& grid)
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::string LLGridManager::getUpdateServiceURL()
|
||||
{
|
||||
std::string update_url_base = gSavedSettings.getString("CmdLineUpdateService");;
|
||||
if (!update_url_base.empty())
|
||||
{
|
||||
LL_INFOS("UpdaterService", "GridManager")
|
||||
<< "Update URL base overridden from command line: " << update_url_base
|
||||
<< LL_ENDL;
|
||||
}
|
||||
else if (mGridList[mGrid].has(GRID_UPDATE_SERVICE_URL))
|
||||
{
|
||||
update_url_base = mGridList[mGrid][GRID_UPDATE_SERVICE_URL].asString();
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("UpdaterService", "GridManager")
|
||||
<< "The grid property '" << GRID_UPDATE_SERVICE_URL
|
||||
<< "' is not defined for the grid '" << mGrid << "'"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
return update_url_base;
|
||||
}
|
||||
|
||||
// get app slurl base for the given region
|
||||
// within the selected grid (LL comment was misleading)
|
||||
std::string LLGridManager::getAppSLURLBase(const std::string& grid)
|
||||
|
|
|
|||
|
|
@ -242,6 +242,8 @@ public:
|
|||
void setDirectoryFee(const S32 directory_fee) { mDirectoryFee = directory_fee; }
|
||||
S32 getDirectoryFee() const { return mDirectoryFee; }
|
||||
|
||||
std::string getUpdateServiceURL();
|
||||
|
||||
private:
|
||||
friend class GridInfoRequestResponder;
|
||||
|
||||
|
|
|
|||
|
|
@ -976,9 +976,22 @@ Function un.UserSettingsFiles
|
|||
|
||||
StrCmp $DO_UNINSTALL_V2 "true" Keep # Don't remove user's settings files on auto upgrade
|
||||
|
||||
# Ask if user wants to keep data files or not
|
||||
MessageBox MB_YESNO|MB_ICONQUESTION $(RemoveDataFilesMB) IDYES Remove IDNO Keep
|
||||
ClearErrors
|
||||
Push $0
|
||||
${GetParameters} $COMMANDLINE
|
||||
${GetOptionsS} $COMMANDLINE "/clrusrfiles" $0
|
||||
# GetOptionsS returns an error if option does not exist, jump past Goto.
|
||||
IfErrors +3 0
|
||||
Pop $0
|
||||
Goto Remove
|
||||
|
||||
Pop $0
|
||||
ClearErrors
|
||||
|
||||
ifSilent Keep 0
|
||||
# Ask if user wants to keep data files or not
|
||||
MessageBox MB_YESNO|MB_ICONQUESTION $(RemoveDataFilesMB) IDYES Remove IDNO Keep
|
||||
|
||||
Remove:
|
||||
Push $0
|
||||
Push $1
|
||||
|
|
@ -1085,14 +1098,28 @@ RMDir "$INSTDIR"
|
|||
IfFileExists "$INSTDIR" FOLDERFOUND NOFOLDER
|
||||
|
||||
FOLDERFOUND:
|
||||
# Silent uninstall always removes all files (/SD IDYES)
|
||||
MessageBox MB_YESNO $(DeleteProgramFilesMB) /SD IDYES IDNO NOFOLDER
|
||||
RMDir /r "$INSTDIR"
|
||||
# Silent uninstall always removes all files (/SD IDYES)
|
||||
ifSilent 2 0
|
||||
MessageBox MB_YESNO $(DeleteProgramFilesMB) /SD IDYES IDNO NOFOLDER
|
||||
RMDir /r "$INSTDIR"
|
||||
|
||||
NOFOLDER:
|
||||
|
||||
MessageBox MB_YESNO $(DeleteRegistryKeysMB) IDYES DeleteKeys IDNO NoDelete
|
||||
ClearErrors
|
||||
Push $0
|
||||
${GetParameters} $COMMANDLINE
|
||||
${GetOptionsS} $COMMANDLINE "/clearreg" $0
|
||||
# GetOptionsS returns an error if option does not exist, jump past Goto.
|
||||
IfErrors +3 0
|
||||
Pop $0
|
||||
Goto DeleteKeys
|
||||
|
||||
Pop $0
|
||||
ClearErrors
|
||||
|
||||
ifSilent NoDelete 0
|
||||
MessageBox MB_YESNO $(DeleteRegistryKeysMB) IDYES DeleteKeys IDNO NoDelete
|
||||
|
||||
DeleteKeys:
|
||||
DeleteRegKey SHELL_CONTEXT "SOFTWARE\Classes\x-grid-info" # <FS:Ansariel> FIRE-30446: Register x-grid-info hypergrid protocol
|
||||
DeleteRegKey SHELL_CONTEXT "SOFTWARE\Classes\x-grid-location-info"
|
||||
|
|
|
|||
|
|
@ -98,6 +98,11 @@
|
|||
#include "llurlmatch.h"
|
||||
#include "lltextutil.h"
|
||||
#include "lllogininstance.h"
|
||||
#include "llvvmquery.h"
|
||||
|
||||
#if LL_VELOPACK
|
||||
#include "llvelopack.h"
|
||||
#endif
|
||||
#include "llprogressview.h"
|
||||
#include "llvocache.h"
|
||||
#include "lldiskcache.h"
|
||||
|
|
@ -1313,103 +1318,53 @@ bool LLAppViewer::init()
|
|||
gGLActive = false;
|
||||
|
||||
// <FS:Ansariel> Disable updater
|
||||
//#if LL_RELEASE_FOR_DOWNLOAD
|
||||
// // Skip updater if this is a non-interactive instance
|
||||
////#if LL_RELEASE_FOR_DOWNLOAD
|
||||
// // Launch VVM update check (replaces SLVersionChecker)
|
||||
// if (!gSavedSettings.getBOOL("CmdLineSkipUpdater") && !gNonInteractive)
|
||||
// {
|
||||
// LLProcess::Params updater;
|
||||
// updater.desc = "updater process";
|
||||
// // Because it's the updater, it MUST persist beyond the lifespan of the
|
||||
// // viewer itself.
|
||||
// updater.autokill = false;
|
||||
// std::string updater_file;
|
||||
//#if LL_WINDOWS
|
||||
// updater_file = "SLVersionChecker.exe";
|
||||
// updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, updater_file);
|
||||
//#elif LL_DARWIN
|
||||
// updater_file = "SLVersionChecker";
|
||||
// updater.executable = gDirUtilp->add(gDirUtilp->getAppRODataDir(), "updater", updater_file);
|
||||
//#else
|
||||
// updater_file = "SLVersionChecker";
|
||||
// updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, updater_file);
|
||||
//#endif
|
||||
// // add LEAP mode command-line argument to whichever of these we selected
|
||||
// updater.args.add("leap");
|
||||
// // UpdaterServiceSettings
|
||||
// if (gSavedSettings.getBOOL("FirstLoginThisInstall"))
|
||||
// {
|
||||
// // Befor first login, treat this as 'manual' updates,
|
||||
// // updater won't install anything, but required updates
|
||||
// updater.args.add("0");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// updater.args.add(stringize(gSavedSettings.getU32("UpdaterServiceSetting")));
|
||||
// }
|
||||
// // channel
|
||||
// updater.args.add(LLVersionInfo::instance().getChannel());
|
||||
// // testok
|
||||
// updater.args.add(stringize(gSavedSettings.getBOOL("UpdaterWillingToTest")));
|
||||
// // ForceAddressSize
|
||||
// updater.args.add(stringize(gSavedSettings.getU32("ForceAddressSize")));
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// // Run the updater. An exception from launching the updater should bother us.
|
||||
// LLLeap::create(updater, true);
|
||||
// mUpdaterNotFound = false;
|
||||
// }
|
||||
// catch (...)
|
||||
// {
|
||||
// LLUIString details = LLNotifications::instance().getGlobalString("LLLeapUpdaterFailure");
|
||||
// details.setArg("[UPDATER_APP]", updater_file);
|
||||
// OSMessageBox(
|
||||
// details.getString(),
|
||||
// LLStringUtil::null,
|
||||
// OSMB_OK);
|
||||
// mUpdaterNotFound = true;
|
||||
// }
|
||||
// initVVMUpdateCheck();
|
||||
// mUpdaterNotFound = false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// LL_WARNS("InitInfo") << "Skipping updater check." << LL_ENDL;
|
||||
// }
|
||||
//#endif //LL_RELEASE_FOR_DOWNLOAD
|
||||
//
|
||||
// {
|
||||
// // Iterate over --leap command-line options. But this is a bit tricky: if
|
||||
// // there's only one, it won't be an array at all.
|
||||
// LLSD LeapCommand(gSavedSettings.getLLSD("LeapCommand"));
|
||||
// LL_DEBUGS("InitInfo") << "LeapCommand: " << LeapCommand << LL_ENDL;
|
||||
// if (LeapCommand.isDefined() && !LeapCommand.isArray())
|
||||
// {
|
||||
// // If LeapCommand is actually a scalar value, make an array of it.
|
||||
// // Have to do it in two steps because LeapCommand.append(LeapCommand)
|
||||
// // trashes content! :-P
|
||||
// LLSD item(LeapCommand);
|
||||
// LeapCommand.append(item);
|
||||
// }
|
||||
// for (const auto& leap : llsd::inArray(LeapCommand))
|
||||
// {
|
||||
// LL_INFOS("InitInfo") << "processing --leap \"" << leap << '"' << LL_ENDL;
|
||||
// // We don't have any better description of this plugin than the
|
||||
// // user-specified command line. Passing "" causes LLLeap to derive a
|
||||
// // description from the command line itself.
|
||||
// // Suppress LLLeap::Error exception: trust LLLeap's own logging. We
|
||||
// // don't consider any one --leap command mission-critical, so if one
|
||||
// // fails, log it, shrug and carry on.
|
||||
// LLLeap::create("", leap, false); // exception=false
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0)
|
||||
// {
|
||||
// LL_WARNS("InitInfo") << "QAModeEventHostPort DEPRECATED: "
|
||||
// << "lleventhost no longer supported as a dynamic library"
|
||||
// << LL_ENDL;
|
||||
// }
|
||||
////#endif //LL_RELEASE_FOR_DOWNLOAD
|
||||
// </FS:Ansariel>
|
||||
|
||||
{
|
||||
// Iterate over --leap command-line options. But this is a bit tricky: if
|
||||
// there's only one, it won't be an array at all.
|
||||
LLSD LeapCommand(gSavedSettings.getLLSD("LeapCommand"));
|
||||
LL_DEBUGS("InitInfo") << "LeapCommand: " << LeapCommand << LL_ENDL;
|
||||
if (LeapCommand.isDefined() && !LeapCommand.isArray())
|
||||
{
|
||||
// If LeapCommand is actually a scalar value, make an array of it.
|
||||
// Have to do it in two steps because LeapCommand.append(LeapCommand)
|
||||
// trashes content! :-P
|
||||
LLSD item(LeapCommand);
|
||||
LeapCommand.append(item);
|
||||
}
|
||||
for (const auto& leap : llsd::inArray(LeapCommand))
|
||||
{
|
||||
LL_INFOS("InitInfo") << "processing --leap \"" << leap << '"' << LL_ENDL;
|
||||
// We don't have any better description of this plugin than the
|
||||
// user-specified command line. Passing "" causes LLLeap to derive a
|
||||
// description from the command line itself.
|
||||
// Suppress LLLeap::Error exception: trust LLLeap's own logging. We
|
||||
// don't consider any one --leap command mission-critical, so if one
|
||||
// fails, log it, shrug and carry on.
|
||||
LLLeap::create("", leap, false); // exception=false
|
||||
}
|
||||
}
|
||||
|
||||
if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0)
|
||||
{
|
||||
LL_WARNS("InitInfo") << "QAModeEventHostPort DEPRECATED: "
|
||||
<< "lleventhost no longer supported as a dynamic library"
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
||||
LLTextUtil::TextHelpers::iconCallbackCreationFunction = create_text_segment_icon_from_url_match;
|
||||
|
||||
//EXT-7013 - On windows for some locale (Japanese) standard
|
||||
|
|
@ -1998,6 +1953,15 @@ void LLAppViewer::flushLFSIO()
|
|||
|
||||
bool LLAppViewer::cleanup()
|
||||
{
|
||||
#if LL_VELOPACK
|
||||
// Apply any pending Velopack update before shutdown
|
||||
if (velopack_is_update_pending())
|
||||
{
|
||||
LL_INFOS("AppInit") << "Applying pending Velopack update on shutdown..." << LL_ENDL;
|
||||
velopack_apply_pending_update(true); // restart=true
|
||||
}
|
||||
#endif
|
||||
|
||||
//ditch LLVOAvatarSelf instance
|
||||
gAgentAvatarp = NULL;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@
|
|||
#include "llappviewer.h"
|
||||
#include "llstartup.h"
|
||||
|
||||
#if LL_VELOPACK && LL_WINDOWS
|
||||
#include "llvelopack.h"
|
||||
#include <shellapi.h>
|
||||
#endif
|
||||
|
||||
#if LL_WINDOWS
|
||||
# include <process.h> // _spawnl()
|
||||
#else
|
||||
|
|
@ -314,6 +319,7 @@ std::unique_ptr<LLViewerStats::PhaseMap> LLStartUp::sPhases(new LLViewerStats::P
|
|||
|
||||
void login_show();
|
||||
void login_callback(S32 option, void* userdata);
|
||||
void uninstall_nsis_if_required();
|
||||
void show_release_notes_if_required();
|
||||
//void show_first_run_dialog(); // <FS:CR> Unused in Firestorm
|
||||
bool first_run_dialog_callback(const LLSD& notification, const LLSD& response);
|
||||
|
|
@ -1392,6 +1398,7 @@ bool idle_startup()
|
|||
LL_DEBUGS("AppInit") << "PeekMessage processed" << LL_ENDL;
|
||||
#endif
|
||||
do_startup_frame();
|
||||
uninstall_nsis_if_required();
|
||||
timeout.reset();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3546,6 +3553,75 @@ void release_notes_coro(const std::string url)
|
|||
LLWeb::loadURLInternal(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a fresh velopack install and
|
||||
* if uninstallation of old viewer is needed.
|
||||
*/
|
||||
void uninstall_nsis_if_required()
|
||||
{
|
||||
#if LL_VELOPACK && LL_WINDOWS
|
||||
// Todo: perhaps use marker files?
|
||||
// Debug variable isn't specific to one channel
|
||||
// and something channel specific is needed.
|
||||
std::string last_install_ver = gSavedSettings.getString("LastInstallVersion");
|
||||
LLVersionInfo* ver_inst = LLVersionInfo::getInstance();
|
||||
if (ver_inst->getChannelAndVersion() == last_install_ver)
|
||||
{
|
||||
return;
|
||||
}
|
||||
gSavedSettings.setString("LastInstallVersion",
|
||||
ver_inst->getChannelAndVersion());
|
||||
|
||||
LL_INFOS() << "Looking for previous NSIS installs" << LL_ENDL;
|
||||
|
||||
wchar_t buffer[MAX_PATH];
|
||||
if (!get_nsis_uninstaller_path( buffer,
|
||||
MAX_PATH,
|
||||
ver_inst->getMajor(),
|
||||
ver_inst->getMinor(),
|
||||
ver_inst->getPatch(),
|
||||
ver_inst->getBuild())
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Compose command line: "<uninstaller_path>" /S /clearreg
|
||||
std::wstring params = L"\"";
|
||||
params += buffer;
|
||||
params += L"\"";
|
||||
// params += L" /S /clearreg"; // silent uninstall and clear registry entries
|
||||
|
||||
LLNotificationsUtil::add("PromptRemoveNsisInstallation", LLSD(), LLSD(),
|
||||
[params](const LLSD& notification, const LLSD& response)
|
||||
{
|
||||
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
|
||||
if (option == 1) // cancel
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LL_INFOS() << "Triggering NSIS uninstall from " << ll_convert_wide_to_string(params) << LL_ENDL;
|
||||
|
||||
// Launch uninstaller using explorer.exe
|
||||
SHELLEXECUTEINFOW sei = { 0 };
|
||||
sei.cbSize = sizeof(sei);
|
||||
sei.fMask = SEE_MASK_DEFAULT;
|
||||
sei.hwnd = NULL;
|
||||
sei.lpVerb = L"runas"; // Request elevation
|
||||
sei.lpFile = L"explorer.exe";
|
||||
sei.lpParameters = params.c_str();
|
||||
|
||||
sei.nShow = SW_HIDE;
|
||||
|
||||
if (!ShellExecuteExW(&sei))
|
||||
{
|
||||
LL_WARNS("AppInit") << "Failed to launch NSIS uninstaller, error code: " << GetLastError() << LL_ENDL;
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void validate_release_notes_coro(const std::string url)
|
||||
{
|
||||
LLVersionInfo& versionInfo(LLVersionInfo::instance());
|
||||
|
|
|
|||
|
|
@ -28,28 +28,39 @@
|
|||
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
#include "llvelopack.h"
|
||||
#include "llstring.h"
|
||||
|
||||
#include "Velopack.h"
|
||||
|
||||
#if LL_WINDOWS
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <shobjidl.h>
|
||||
#include <shlwapi.h>
|
||||
#include <objbase.h>
|
||||
|
||||
#include "Velopack.h"
|
||||
#include <filesystem>
|
||||
|
||||
#pragma comment(lib, "shlwapi.lib")
|
||||
#pragma comment(lib, "ole32.lib")
|
||||
#pragma comment(lib, "shell32.lib")
|
||||
#endif // LL_WINDOWS
|
||||
|
||||
static const wchar_t* PROTOCOL_SECONDLIFE = L"secondlife";
|
||||
static const wchar_t* PROTOCOL_GRID_INFO = L"x-grid-location-info";
|
||||
static const wchar_t* VIEWER_EXE_NAME = L"SecondLifeViewer.exe";
|
||||
|
||||
// Common state
|
||||
static std::string sUpdateUrl;
|
||||
static std::function<void(int)> sProgressCallback;
|
||||
static vpkc_update_manager_t* sUpdateManager = nullptr;
|
||||
static vpkc_update_info_t* sPendingUpdate = nullptr;
|
||||
|
||||
//
|
||||
// Platform-specific helpers and hooks
|
||||
//
|
||||
|
||||
#if LL_WINDOWS
|
||||
|
||||
static const wchar_t* PROTOCOL_SECONDLIFE = L"secondlife";
|
||||
static const wchar_t* PROTOCOL_GRID_INFO = L"x-grid-location-info";
|
||||
static const wchar_t* VIEWER_EXE_NAME = L"SecondLifeViewer.exe";
|
||||
|
||||
static std::wstring get_install_dir()
|
||||
{
|
||||
wchar_t path[MAX_PATH];
|
||||
|
|
@ -166,6 +177,89 @@ static void register_protocol_handler(const std::wstring& protocol,
|
|||
}
|
||||
}
|
||||
|
||||
static void parse_version(const wchar_t* version_str, int& major, int& minor, int& patch, uint64_t& build)
|
||||
{
|
||||
major = minor = patch = 0;
|
||||
build = 0;
|
||||
if (!version_str) return;
|
||||
// Use swscanf for wide strings
|
||||
swscanf(version_str, L"%d.%d.%d.%llu", &major, &minor, &patch, &build);
|
||||
}
|
||||
|
||||
bool get_nsis_uninstaller_path(wchar_t* path_buffer, DWORD bufSize, S32 cur_major_ver, S32 cur_minor_ver, S32 cur_patch_ver, U64 cur_build_ver)
|
||||
{
|
||||
// Test for presence of NSIS viewer registration, then
|
||||
// attempt to read uninstall info
|
||||
std::wstring app_name_oneword = get_app_name_oneword();
|
||||
std::wstring uninstall_key_path = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + app_name_oneword;
|
||||
HKEY hkey;
|
||||
LONG result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, uninstall_key_path.c_str(), 0, KEY_READ, &hkey);
|
||||
if (result != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read DisplayVersion
|
||||
wchar_t version_buf[64] = { 0 };
|
||||
DWORD version_buf_size = sizeof(version_buf);
|
||||
DWORD type = 0;
|
||||
LONG ver_rv = RegGetValueW(hkey, nullptr, L"DisplayVersion", RRF_RT_REG_SZ, &type, version_buf, &version_buf_size);
|
||||
|
||||
if (ver_rv != ERROR_SUCCESS)
|
||||
{
|
||||
RegCloseKey(hkey);
|
||||
return false;
|
||||
}
|
||||
|
||||
int nsis_major = 0, nsis_minor = 0, nsis_patch = 0;
|
||||
uint64_t nsis_build = 0;
|
||||
parse_version(version_buf, nsis_major, nsis_minor, nsis_patch, nsis_build);
|
||||
|
||||
// Compare numerically
|
||||
if ((nsis_major > cur_major_ver) ||
|
||||
(nsis_major == cur_major_ver && nsis_minor > cur_minor_ver) ||
|
||||
(nsis_major == cur_major_ver && nsis_minor == cur_minor_ver && nsis_patch > cur_patch_ver) ||
|
||||
// Assume that bigger build number means newer version, which is not always true but works for our purposes
|
||||
(nsis_major == cur_major_ver && nsis_minor == cur_minor_ver && nsis_patch == cur_patch_ver && nsis_build > cur_build_ver))
|
||||
{
|
||||
LL_INFOS() << "Found installed nsis version that is newer" << nsis_major << "." << nsis_minor << "." << nsis_patch << LL_ENDL;
|
||||
RegCloseKey(hkey);
|
||||
return false;
|
||||
}
|
||||
|
||||
LONG rv = RegGetValueW(hkey, nullptr, L"UninstallString", RRF_RT_REG_SZ, &type, path_buffer, &bufSize);
|
||||
RegCloseKey(hkey);
|
||||
if (rv != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
size_t len = wcslen(path_buffer);
|
||||
if (len > 0)
|
||||
{
|
||||
if (path_buffer[0] == L'\"')
|
||||
{
|
||||
// Likely to contain leading "
|
||||
memmove(path_buffer, path_buffer + 1, len * sizeof(wchar_t));
|
||||
}
|
||||
wchar_t* pos = wcsstr(path_buffer, L"uninst.exe");
|
||||
if (pos)
|
||||
{
|
||||
// Likely to contain trailing "
|
||||
pos[wcslen(L"uninst.exe")] = L'\0';
|
||||
}
|
||||
}
|
||||
std::error_code ec;
|
||||
std::filesystem::path path(path_buffer);
|
||||
if (!std::filesystem::exists(path, ec))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Todo: check codesigning?
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void unregister_protocol_handler(const std::wstring& protocol)
|
||||
{
|
||||
std::wstring key_path = L"SOFTWARE\\Classes\\" + protocol;
|
||||
|
|
@ -197,6 +291,18 @@ static void register_uninstall_info(const std::wstring& install_dir,
|
|||
RegSetValueExW(hkey, L"DisplayIcon", 0, REG_SZ,
|
||||
(BYTE*)exe_path.c_str(), (DWORD)((exe_path.size() + 1) * sizeof(wchar_t)));
|
||||
|
||||
std::wstring link_url = L"https://support.secondlife.com/contact-support/";
|
||||
RegSetValueExW(hkey, L"HelpLink", 0, REG_SZ,
|
||||
(BYTE*)link_url.c_str(), (DWORD)((link_url.size() + 1) * sizeof(wchar_t)));
|
||||
|
||||
link_url = L"https://secondlife.com/whatis/";
|
||||
RegSetValueExW(hkey, L"URLInfoAbout", 0, REG_SZ,
|
||||
(BYTE*)link_url.c_str(), (DWORD)((link_url.size() + 1) * sizeof(wchar_t)));
|
||||
|
||||
link_url = L"http://secondlife.com/support/downloads/";
|
||||
RegSetValueExW(hkey, L"URLUpdateInfo", 0, REG_SZ,
|
||||
(BYTE*)link_url.c_str(), (DWORD)((link_url.size() + 1) * sizeof(wchar_t)));
|
||||
|
||||
DWORD no_modify = 1;
|
||||
RegSetValueExW(hkey, L"NoModify", 0, REG_DWORD, (BYTE*)&no_modify, sizeof(DWORD));
|
||||
RegSetValueExW(hkey, L"NoRepair", 0, REG_DWORD, (BYTE*)&no_modify, sizeof(DWORD));
|
||||
|
|
@ -275,6 +381,35 @@ static void on_log_message(void* user_data, const char* level, const char* messa
|
|||
OutputDebugStringA("\n");
|
||||
}
|
||||
|
||||
#elif LL_DARWIN
|
||||
|
||||
// macOS-specific hooks
|
||||
// TODO: Implement protocol handler registration via Launch Services
|
||||
// TODO: Implement app bundle management
|
||||
|
||||
static void on_after_install(void* user_data, const char* app_version)
|
||||
{
|
||||
// macOS handles protocol registration via Info.plist CFBundleURLTypes
|
||||
// No additional registration needed at runtime
|
||||
LL_INFOS("Velopack") << "macOS post-install hook called for version: " << app_version << LL_ENDL;
|
||||
}
|
||||
|
||||
static void on_before_uninstall(void* user_data, const char* app_version)
|
||||
{
|
||||
LL_INFOS("Velopack") << "macOS pre-uninstall hook called for version: " << app_version << LL_ENDL;
|
||||
}
|
||||
|
||||
static void on_log_message(void* user_data, const char* level, const char* message)
|
||||
{
|
||||
LL_INFOS("Velopack") << "[" << level << "] " << message << LL_ENDL;
|
||||
}
|
||||
|
||||
#endif // LL_WINDOWS / LL_DARWIN
|
||||
|
||||
//
|
||||
// Common progress callback
|
||||
//
|
||||
|
||||
static void on_progress(void* user_data, size_t progress)
|
||||
{
|
||||
if (sProgressCallback)
|
||||
|
|
@ -283,11 +418,19 @@ static void on_progress(void* user_data, size_t progress)
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Public API - Cross-platform
|
||||
//
|
||||
|
||||
bool velopack_initialize()
|
||||
{
|
||||
vpkc_set_logger(on_log_message, nullptr);
|
||||
|
||||
#if LL_WINDOWS || LL_DARWIN
|
||||
vpkc_app_set_hook_after_install(on_after_install);
|
||||
vpkc_app_set_hook_before_uninstall(on_before_uninstall);
|
||||
#endif
|
||||
|
||||
vpkc_app_run(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -296,6 +439,7 @@ void velopack_check_for_updates()
|
|||
{
|
||||
if (sUpdateUrl.empty())
|
||||
{
|
||||
LL_DEBUGS("Velopack") << "No update URL set, skipping update check" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -307,6 +451,7 @@ void velopack_check_for_updates()
|
|||
|
||||
if (!vpkc_new_update_manager(sUpdateUrl.c_str(), &options, nullptr, &sUpdateManager))
|
||||
{
|
||||
LL_WARNS("Velopack") << "Failed to create update manager" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -316,6 +461,7 @@ void velopack_check_for_updates()
|
|||
|
||||
if (result == UPDATE_AVAILABLE && update_info)
|
||||
{
|
||||
LL_INFOS("Velopack") << "Update available, downloading..." << LL_ENDL;
|
||||
if (vpkc_download_updates(sUpdateManager, update_info, on_progress, nullptr))
|
||||
{
|
||||
if (sPendingUpdate)
|
||||
|
|
@ -323,12 +469,18 @@ void velopack_check_for_updates()
|
|||
vpkc_free_update_info(sPendingUpdate);
|
||||
}
|
||||
sPendingUpdate = update_info;
|
||||
LL_INFOS("Velopack") << "Update downloaded and pending" << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Velopack") << "Failed to download update" << LL_ENDL;
|
||||
vpkc_free_update_info(update_info);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_DEBUGS("Velopack") << "No update available (result=" << result << ")" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
std::string velopack_get_current_version()
|
||||
|
|
@ -356,9 +508,11 @@ void velopack_apply_pending_update(bool restart)
|
|||
{
|
||||
if (!sUpdateManager || !sPendingUpdate || !sPendingUpdate->TargetFullRelease)
|
||||
{
|
||||
LL_WARNS("Velopack") << "Cannot apply update: no pending update or manager" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
LL_INFOS("Velopack") << "Applying pending update (restart=" << restart << ")" << LL_ENDL;
|
||||
vpkc_wait_exit_then_apply_updates(sUpdateManager,
|
||||
sPendingUpdate->TargetFullRelease,
|
||||
false,
|
||||
|
|
@ -369,6 +523,7 @@ void velopack_apply_pending_update(bool restart)
|
|||
void velopack_set_update_url(const std::string& url)
|
||||
{
|
||||
sUpdateUrl = url;
|
||||
LL_INFOS("Velopack") << "Update URL set to: " << url << LL_ENDL;
|
||||
}
|
||||
|
||||
void velopack_set_progress_callback(std::function<void(int)> callback)
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ void velopack_apply_pending_update(bool restart = true);
|
|||
void velopack_set_update_url(const std::string& url);
|
||||
void velopack_set_progress_callback(std::function<void(int)> callback);
|
||||
|
||||
#if LL_WINDOWS
|
||||
bool get_nsis_uninstaller_path(wchar_t* path_buffer, DWORD bufSize, S32 cur_major_ver, S32 cur_minor_ver, S32 cur_patch_ver, U64 cur_build_ver);
|
||||
#endif
|
||||
|
||||
#endif // LL_VELOPACK
|
||||
#endif
|
||||
// EOF
|
||||
|
|
|
|||
|
|
@ -0,0 +1,159 @@
|
|||
/**
|
||||
* @file llvvmquery.cpp
|
||||
* @brief Query the Viewer Version Manager (VVM) for update information
|
||||
*
|
||||
* $LicenseInfo:firstyear=2025&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2025, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
#include "llvvmquery.h"
|
||||
|
||||
#include "llcorehttputil.h"
|
||||
#include "llcoros.h"
|
||||
#include "llevents.h"
|
||||
#include "llviewernetwork.h"
|
||||
#include "llversioninfo.h"
|
||||
#include "llviewercontrol.h"
|
||||
#include "llhasheduniqueid.h"
|
||||
#include "lluri.h"
|
||||
#include "llsys.h"
|
||||
|
||||
#if LL_VELOPACK
|
||||
#include "llvelopack.h"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string get_platform_string()
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
return "win64";
|
||||
#elif LL_DARWIN
|
||||
return "mac64";
|
||||
#elif LL_LINUX
|
||||
return "lnx64";
|
||||
#else
|
||||
return "unknown";
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string get_platform_version()
|
||||
{
|
||||
return LLOSInfo::instance().getOSVersionString();
|
||||
}
|
||||
|
||||
std::string get_machine_id()
|
||||
{
|
||||
unsigned char id[MD5HEX_STR_SIZE];
|
||||
if (llHashedUniqueID(id))
|
||||
{
|
||||
return std::string(reinterpret_cast<char*>(id));
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
void query_vvm_coro()
|
||||
{
|
||||
// Get base URL from grid manager
|
||||
std::string base_url = LLGridManager::getInstance()->getUpdateServiceURL();
|
||||
if (base_url.empty())
|
||||
{
|
||||
LL_WARNS("VVM") << "No update service URL configured" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
// Gather parameters for VVM query
|
||||
std::string channel = LLVersionInfo::instance().getChannel();
|
||||
std::string version = LLVersionInfo::instance().getVersion();
|
||||
std::string platform = get_platform_string();
|
||||
std::string platform_version = get_platform_version();
|
||||
std::string test_ok = gSavedSettings.getBOOL("UpdaterWillingToTest") ? "testok" : "testno";
|
||||
std::string machine_id = get_machine_id();
|
||||
|
||||
// Build URL: {base}/v1.2/{channel}/{version}/{platform}/{platform_version}/{testok}/{uuid}
|
||||
std::string url = base_url + "/v1.2/" +
|
||||
LLURI::escape(channel) + "/" +
|
||||
LLURI::escape(version) + "/" +
|
||||
platform + "/" +
|
||||
LLURI::escape(platform_version) + "/" +
|
||||
test_ok + "/" +
|
||||
machine_id;
|
||||
|
||||
LL_INFOS("VVM") << "Querying VVM: " << url << LL_ENDL;
|
||||
|
||||
// Make HTTP GET request
|
||||
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
|
||||
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter =
|
||||
std::make_shared<LLCoreHttpUtil::HttpCoroutineAdapter>("VVMQuery", httpPolicy);
|
||||
LLCore::HttpRequest::ptr_t request = std::make_shared<LLCore::HttpRequest>();
|
||||
|
||||
LLSD result = adapter->getAndSuspend(request, url);
|
||||
|
||||
// Check HTTP status
|
||||
LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
|
||||
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
|
||||
|
||||
if (!status)
|
||||
{
|
||||
if (status.getType() == 404)
|
||||
{
|
||||
LL_INFOS("VVM") << "Unmanaged channel, no updates available" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
LL_WARNS("VVM") << "VVM query failed: " << status.toString() << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract update URL for current platform
|
||||
LLSD platforms = result["platforms"];
|
||||
if (platforms.has(platform))
|
||||
{
|
||||
std::string update_url = platforms[platform]["url"].asString();
|
||||
if (!update_url.empty())
|
||||
{
|
||||
LL_INFOS("VVM") << "Update available at: " << update_url << LL_ENDL;
|
||||
#if LL_VELOPACK
|
||||
velopack_set_update_url(update_url);
|
||||
velopack_check_for_updates();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_INFOS("VVM") << "No update available for platform: " << platform << LL_ENDL;
|
||||
}
|
||||
|
||||
// Post release notes URL to the relnotes event pump
|
||||
std::string relnotes = result["more_info"].asString();
|
||||
if (!relnotes.empty())
|
||||
{
|
||||
LL_INFOS("VVM") << "Release notes URL: " << relnotes << LL_ENDL;
|
||||
LLEventPumps::instance().obtain("relnotes").post(relnotes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initVVMUpdateCheck()
|
||||
{
|
||||
LL_INFOS("VVM") << "Initializing VVM update check" << LL_ENDL;
|
||||
LLCoros::instance().launch("VVMUpdateCheck", &query_vvm_coro);
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* @file llvvmquery.h
|
||||
* @brief Query the Viewer Version Manager (VVM) for update information
|
||||
*
|
||||
* $LicenseInfo:firstyear=2025&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2025, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLVVMQUERY_H
|
||||
#define LL_LLVVMQUERY_H
|
||||
|
||||
/**
|
||||
* Initialize the VVM update check.
|
||||
*
|
||||
* This launches a coroutine that queries the Viewer Version Manager (VVM)
|
||||
* to check for available updates. If an update is available, it configures
|
||||
* Velopack with the update URL and initiates the update check/download.
|
||||
*
|
||||
* The release notes URL from the VVM response is posted to the "relnotes"
|
||||
* event pump for display.
|
||||
*/
|
||||
void initVVMUpdateCheck();
|
||||
|
||||
#endif // LL_LLVVMQUERY_H
|
||||
|
|
@ -200,6 +200,20 @@ No tutorial is currently available.
|
|||
yestext="OK"/>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="alertmodal.tga"
|
||||
name="PromptRemoveNsisInstallation"
|
||||
type="alertmodal">
|
||||
[APP_NAME] found an installation from an older version. Do you want to uninstall the previous version now?
|
||||
|
||||
The uninstaller may display additional prompts requesting permission to access or modify files on your disk.
|
||||
<tag>confirm</tag>
|
||||
<usetemplate
|
||||
name="okcancelbuttons"
|
||||
notext="Cancel"
|
||||
yestext="Uninstall"/>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="alertmodal.tga"
|
||||
name="LoginFailedNoNetwork"
|
||||
|
|
|
|||
|
|
@ -1036,11 +1036,13 @@ class Windows_x86_64_Manifest(ViewerManifest):
|
|||
releases_dir = os.path.join(os.path.dirname(pack_dir), 'Releases')
|
||||
|
||||
# Move the setup exe INTO pack_dir so it's included in the Windows-app artifact
|
||||
# Use hyphen format (-Setup.exe) to avoid the *_Setup.exe exclusion pattern in viewer_app
|
||||
# IMPORTANT: Use hyphen format (-Setup.exe) to avoid the *_Setup.exe exclusion pattern
|
||||
# in viewer_app output (line ~538). The underscore pattern excludes NSIS installers
|
||||
# which are rebuilt during signing, but Velopack installers are created here.
|
||||
# Velopack creates: {packId}-win-Setup.exe
|
||||
velopack_setup = os.path.join(releases_dir, '%s-win-Setup.exe' % pack_id)
|
||||
# Keep Velopack naming convention (hyphen, not underscore)
|
||||
self.package_file = '%s-Setup.exe' % pack_id
|
||||
# Use versioned name with hyphen: Second_Life_26_1_0_53294_x86_64-Setup.exe
|
||||
self.package_file = self.installer_base_name() + '-Setup.exe'
|
||||
our_setup = os.path.join(pack_dir, self.package_file)
|
||||
if os.path.exists(velopack_setup):
|
||||
shutil.move(velopack_setup, our_setup)
|
||||
|
|
@ -1949,6 +1951,105 @@ class Darwin_x86_64_Manifest(ViewerManifest):
|
|||
self.remove(sparsename)
|
||||
self.fs_save_osx_symbols()
|
||||
|
||||
# Generate Velopack update packages if enabled
|
||||
# This creates the nupkg and RELEASES files needed for auto-updates
|
||||
# Distribution is still via DMG, but updates use Velopack
|
||||
if self.args.get('velopack', 'OFF') == 'ON':
|
||||
self.velopack_package_finish()
|
||||
|
||||
def velopack_package_finish(self):
|
||||
"""Generate Velopack update packages for macOS.
|
||||
|
||||
This creates the nupkg and releases.json files needed for auto-updates.
|
||||
Distribution is still via DMG - Velopack only handles the update infrastructure.
|
||||
"""
|
||||
# packId determines install identification - same as Windows for consistency
|
||||
pack_id = self.app_name_oneword() # "SecondLife", "SecondLifeBeta", etc.
|
||||
# Velopack requires SemVer2 (3-part: major.minor.patch), viewer has 4 parts
|
||||
pack_version = '.'.join(self.args['version'][:3])
|
||||
pack_title = self.app_name() # Display name with spaces
|
||||
|
||||
# The .app bundle path (e.g., "/path/to/Second Life Release.app")
|
||||
app_bundle = self.get_dst_prefix()
|
||||
# Parent directory containing the .app bundle - this is where we run vpk from
|
||||
# and where the Releases directory will be created
|
||||
work_dir = os.path.dirname(app_bundle)
|
||||
# Bundle ID from args (e.g., "com.secondlife.viewer")
|
||||
bundle_id = self.args.get('bundleid', 'com.secondlife.indra.viewer')
|
||||
|
||||
# Output directory for releases - clean it first to avoid version conflicts
|
||||
releases_dir = os.path.join(work_dir, 'Releases')
|
||||
if os.path.exists(releases_dir):
|
||||
print("Cleaning existing Releases directory: %s" % releases_dir)
|
||||
shutil.rmtree(releases_dir)
|
||||
|
||||
# Icon path for macOS
|
||||
icon_path = os.path.join(self.get_src_prefix(), self.icon_path(), 'secondlife.icns')
|
||||
|
||||
# The main executable inside Contents/MacOS/ is named after the channel
|
||||
main_exe = self.channel()
|
||||
|
||||
# Build vpk command for macOS
|
||||
# See: https://docs.velopack.io/reference/cli/content/vpk-osx
|
||||
vpk_args = [
|
||||
'vpk', 'pack',
|
||||
'--packId', pack_id,
|
||||
'--packVersion', pack_version,
|
||||
'--packDir', app_bundle,
|
||||
'--packTitle', pack_title,
|
||||
'--mainExe', main_exe, # Executable name inside Contents/MacOS/
|
||||
'--bundleId', bundle_id,
|
||||
'--outputDir', releases_dir,
|
||||
'--noInst', # Don't generate .pkg installer - we use DMG for distribution
|
||||
'--verbose', # Show detailed output
|
||||
]
|
||||
|
||||
# Add icon if exists
|
||||
if os.path.exists(icon_path):
|
||||
vpk_args.extend(['--icon', icon_path])
|
||||
|
||||
print("Running Velopack packaging for macOS:")
|
||||
print(" Command: %s" % ' '.join(vpk_args))
|
||||
print(" Working directory: %s" % work_dir)
|
||||
print(" App bundle: %s" % app_bundle)
|
||||
print(" Main executable: %s" % main_exe)
|
||||
|
||||
# Run vpk command
|
||||
result = subprocess.run(vpk_args, cwd=work_dir, capture_output=True, text=True)
|
||||
|
||||
# Always print output for debugging
|
||||
if result.stdout:
|
||||
print("vpk stdout:\n%s" % result.stdout)
|
||||
if result.stderr:
|
||||
print("vpk stderr:\n%s" % result.stderr)
|
||||
|
||||
if result.returncode != 0:
|
||||
raise ManifestError("Velopack packaging failed with code %d" % result.returncode)
|
||||
|
||||
# Verify the Releases directory was created and contains expected files
|
||||
if not os.path.exists(releases_dir):
|
||||
raise ManifestError("Velopack releases directory not found: %s" % releases_dir)
|
||||
|
||||
# List what was created
|
||||
releases_contents = os.listdir(releases_dir)
|
||||
print("Velopack releases directory contents: %s" % releases_contents)
|
||||
|
||||
# Verify we have the expected files (nupkg and releases JSON)
|
||||
nupkg_files = [f for f in releases_contents if f.endswith('.nupkg')]
|
||||
json_files = [f for f in releases_contents if f.endswith('.json')]
|
||||
|
||||
if not nupkg_files:
|
||||
raise ManifestError("No .nupkg files found in releases directory")
|
||||
if not json_files:
|
||||
raise ManifestError("No releases JSON files found in releases directory")
|
||||
|
||||
print("Generated %d nupkg file(s): %s" % (len(nupkg_files), nupkg_files))
|
||||
print("Generated %d JSON file(s): %s" % (len(json_files), json_files))
|
||||
|
||||
# Output the Releases directory path for artifact upload
|
||||
self.set_github_output('velopack_releases', releases_dir)
|
||||
print("Velopack releases directory: %s" % releases_dir)
|
||||
|
||||
|
||||
class LinuxManifest(ViewerManifest):
|
||||
build_data_json_platform = 'lnx'
|
||||
|
|
|
|||
Loading…
Reference in New Issue