# Conflicts:
#	.github/workflows/build.yaml
#	indra/cmake/Python.cmake
#	indra/cmake/Velopack.cmake
#	indra/lib/python/indra/util/llmanifest.py
#	indra/newview/CMakeLists.txt
#	indra/newview/app_settings/settings.xml
#	indra/newview/installers/windows/installer_template.nsi
#	indra/newview/llappviewer.cpp
#	indra/newview/llappviewerwin32.cpp
#	indra/newview/llpanellogin.cpp
#	indra/newview/llstartup.cpp
#	indra/newview/llvelopack.cpp
#	indra/newview/llvelopack.h
#	indra/newview/skins/default/xui/en/notifications.xml
#	indra/newview/viewer_manifest.py
This commit is contained in:
Ansariel
2026-04-08 22:09:24 +02:00
13 changed files with 232 additions and 151 deletions
+2 -2
View File
@@ -345,7 +345,7 @@ jobs:
steps:
- name: Sign and package Windows viewer
if: env.AZURE_KEY_VAULT_URI && env.AZURE_CERT_NAME && env.AZURE_CLIENT_ID && env.AZURE_CLIENT_SECRET && env.AZURE_TENANT_ID
uses: secondlife/viewer-build-util/sign-pkg-windows@geenz/velopack
uses: secondlife/viewer-build-util/sign-pkg-windows@v2.1.0
with:
vault_uri: "${{ env.AZURE_KEY_VAULT_URI }}"
cert_name: "${{ env.AZURE_CERT_NAME }}"
@@ -392,7 +392,7 @@ jobs:
- name: Sign and package Mac viewer
if: env.SIGNING_CERT_MACOS && env.SIGNING_CERT_MACOS_IDENTITY && env.SIGNING_CERT_MACOS_PASSWORD && steps.note-creds.outputs.note_user && steps.note-creds.outputs.note_pass && steps.note-creds.outputs.note_team
uses: secondlife/viewer-build-util/sign-pkg-mac@geenz/velopack
uses: secondlife/viewer-build-util/sign-pkg-mac@v2.1.0
with:
channel: ${{ needs.build.outputs.viewer_channel }}
imagename: ${{ needs.build.outputs.imagename }}
+28 -8
View File
@@ -21,7 +21,9 @@ on:
project:
description: "Project Name (used for channel name in project builds, and tag name for all builds)"
default: "hippo"
# TODO - add an input for selecting another sha to build other than head of branch
tag_override:
description: "Override the tag name (optional). If the tag already exists, a numeric suffix is appended."
required: false
jobs:
tag-release:
@@ -34,7 +36,7 @@ jobs:
NIGHTLY_DATE=$(date --rfc-3339=date)
echo NIGHTLY_DATE=${NIGHTLY_DATE} >> ${GITHUB_ENV}
echo TAG_ID="$(echo ${{ github.sha }} | cut -c1-8)-${{ inputs.project || '${NIGHTLY_DATE}' }}" >> ${GITHUB_ENV}
- name: Update Tag
- name: Create Tag
uses: actions/github-script@v8
with:
# use a real access token instead of GITHUB_TOKEN default.
@@ -44,9 +46,27 @@ jobs:
# this token will need to be renewed anually in January
github-token: ${{ secrets.LL_TAG_RELEASE_TOKEN }}
script: |
github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: "refs/tags/${{ env.VIEWER_CHANNEL }}#${{ env.TAG_ID }}",
sha: context.sha
})
const override = `${{ inputs.tag_override }}`.trim();
const baseTag = override || `${{ env.VIEWER_CHANNEL }}#${{ env.TAG_ID }}`;
// Try the base tag first, then append -2, -3, etc. if it already exists
let tag = baseTag;
for (let attempt = 1; ; attempt++) {
try {
await github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `refs/tags/${tag}`,
sha: context.sha
});
core.info(`Created tag: ${tag}`);
break;
} catch (e) {
if (e.status === 422 && attempt < 10) {
core.info(`Tag '${tag}' already exists, trying next suffix...`);
tag = `${baseTag}-${attempt + 1}`;
} else {
throw e;
}
}
}
+13 -2
View File
@@ -6965,6 +6965,17 @@
<key>Backup</key>
<integer>0</integer>
</map>
<key>PreviousInstallChecked</key>
<map>
<key>Comment</key>
<string>Whether viewer checked previous install on the same channel for NSIS</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>LastInstallVersion</key>
<map>
<key>Comment</key>
@@ -17267,11 +17278,11 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>UpdaterShowReleaseNotes</key>
<map>
<key>Comment</key>
<string>Enables displaying of the Release notes in a web floater after update.</string>
<string>Enables displaying of the Release notes in a web floater after update. 0 - don't show, 1 - show, 2 - show even for test viewers</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<string>S32</string>
<key>Value</key>
<integer>0</integer>
</map>
@@ -1172,21 +1172,21 @@ Function .onInstSuccess
# </FS:Ansariel>
Call CheckWindowsServPack # Warn if not on the latest SP before asking to launch.
# <FS:PP> Disable autorun
#StrCmp $SKIP_AUTORUN "true" +2;
StrCmp $SKIP_DIALOGS "true" label_launch
${GetOptions} $COMMANDLINE "/AUTOSTART" $R0
# If parameter was there (no error) just launch
# Otherwise ask
IfErrors label_ask_launch label_launch
label_ask_launch:
# Don't launch by default when silent
IfSilent label_no_launch
MessageBox MB_YESNO $(InstSuccesssQuestion) IDYES label_launch IDNO label_no_launch
label_launch:
# <FS:PP> Disable autorun
#StrCmp $SKIP_AUTORUN "true" +2;
StrCmp $SKIP_DIALOGS "true" label_launch
${GetOptions} $COMMANDLINE "/AUTOSTART" $R0
# If parameter was there (no error) just launch
# Otherwise ask
IfErrors label_ask_launch label_launch
label_ask_launch:
# Don't launch by default when silent
IfSilent label_no_launch
MessageBox MB_YESNO $(InstSuccesssQuestion) IDYES label_launch IDNO label_no_launch
label_launch:
Exec '"$INSTDIR\$VIEWER_EXE" $SHORTCUT_LANG_PARAM'
label_no_launch:
+1 -13
View File
@@ -443,18 +443,6 @@ const std::string ERROR_MARKER_FILE_NAME(SAFE_FILE_NAME_PREFIX + ".error_marker"
const std::string LOGOUT_MARKER_FILE_NAME(SAFE_FILE_NAME_PREFIX + ".logout_marker"); //FS orig modified LL
static std::string gLaunchFileOnQuit;
// Used on Win32 for other apps to identify our window (eg, win_setup)
#if LL_VELOPACK
// Velopack is deliberately different fron NSIS to not prevent nsis uninstall
const char* const VIEWER_WINDOW_CLASSNAME = "Second\u00A0Life"; // no break space
#else
// NSIS relies on this to detect if viewer is up.
// NSIS's method is somewhat unreliable since window
// can close long before cleanup is done
// Note: Changing this breaks compatibility with SLURL handling, try to avoid it.
const char* const VIEWER_WINDOW_CLASSNAME = "Second Life";
#endif
//----------------------------------------------------------------------------
// List of entries from strings.xml to always replace
@@ -3740,7 +3728,7 @@ bool LLAppViewer::initWindow()
LLViewerWindow::Params window_params;
window_params
.title(gWindowTitle)
.name(VIEWER_WINDOW_CLASSNAME)
.name(sWindowClass)
.x(gSavedSettings.getS32("WindowX"))
.y(gSavedSettings.getS32("WindowY"))
.width(gSavedSettings.getU32("WindowWidth"))
+8
View File
@@ -287,6 +287,14 @@ protected:
virtual void sendOutOfDiskSpaceNotification();
protected:
// NSIS relies on this to detect if viewer is up.
// NSIS's method is somewhat unreliable since window
// can close long before cleanup is done.
// sendURLToOtherInstance also relies on this to detect if viewer is up.
static constexpr const char* sWindowClass = "Second Life";
private:
bool doFrame();
+4 -4
View File
@@ -291,7 +291,6 @@ LONG WINAPI catchallCrashHandler(EXCEPTION_POINTERS * /*ExceptionInfo*/)
return 0;
}
const std::string LLAppViewerWin32::sWindowClass = "Second Life";
/*
This function is used to print to the command line a text message
@@ -509,9 +508,10 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
LLControlGroup settings("global");
if (settings.loadFromFile(user_settings_path))
{
if (settings.controlExists("LastInstallVersion"))
// If user reinstalls or updates, we want to recheck for nsis leftovers.
if (settings.controlExists("PreviousInstallChecked"))
{
settings.setString("LastInstallVersion", std::string());
settings.setBOOL("PreviousInstallChecked", false);
}
settings.saveToFile(user_settings_path, true);
}
@@ -1207,7 +1207,7 @@ bool LLAppViewerWin32::restoreErrorTrap()
bool LLAppViewerWin32::sendURLToOtherInstance(const std::string& url)
{
wchar_t window_class[256]; /* Flawfinder: ignore */ // Assume max length < 255 chars.
mbstowcs(window_class, sWindowClass.c_str(), 255);
mbstowcs(window_class, sWindowClass, 255);
window_class[255] = 0;
// Use the class instead of the window name.
HWND other_window = FindWindow(window_class, NULL);
-2
View File
@@ -59,8 +59,6 @@ protected:
std::string generateSerialNumber();
static const std::string sWindowClass;
private:
void disableWinErrorReporting();
void bugsplatAddStaticAttributes(const LLSD& info) override; // <FS:Beq> override for windows attributes
+49 -54
View File
@@ -3563,72 +3563,57 @@ void release_notes_coro(const std::string url)
void uninstall_nsis_if_required()
{
#if LL_VELOPACK && LL_WINDOWS
std::string last_install_ver = gSavedSettings.getString("LastInstallVersion");
if (!last_install_ver.empty())
bool checked_for_legacy_install = gSavedSettings.getBOOL("PreviousInstallChecked");
if (checked_for_legacy_install)
{
return;
}
LLVersionInfo* ver_inst = LLVersionInfo::getInstance();
gSavedSettings.setString("LastInstallVersion",
ver_inst->getChannelAndVersion());
if (LLNotifications::instance().getIgnored("PromptRemoveNsisInstallation"))
{
// By default 'ignore' returns default button, which is uninstall
// for PromptRemoveNsisInstallation, but while we want the button
// to be the default, we don't want a scary UAC without a notice
// as a default action, so if this notification is ignored,
// we will treat it as if user is going to cancel the uninstall.
return;
}
gSavedSettings.setBOOL("PreviousInstallChecked", true);
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())
)
S32 found_major = 0;
S32 found_minor = 0;
S32 found_patch = 0;
U64 found_build = 0;
if (!get_nsis_version(found_major, found_minor, found_patch, found_build))
{
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
LLVersionInfo* ver_inst = LLVersionInfo::getInstance();
LLNotificationsUtil::add("PromptRemoveNsisInstallation", LLSD(), LLSD(),
[params](const LLSD& notification, const LLSD& response)
if (found_major > ver_inst->getMajor())
{
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if (option == 1) // cancel
{
return;
}
LL_INFOS() << "Found installed nsis version that is newer" << found_major << "." << found_minor << "." << found_patch << "." << found_build << LL_ENDL;
return;
}
LL_INFOS() << "Triggering NSIS uninstall from " << ll_convert_wide_to_string(params) << LL_ENDL;
if (found_major == ver_inst->getMajor()
&& found_minor > ver_inst->getMinor())
{
LL_INFOS() << "Found installed nsis version that is newer" << found_major << "." << found_minor << "." << found_patch << "." << found_build << LL_ENDL;
return;
}
// 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();
if (found_major == ver_inst->getMajor()
&& found_minor == ver_inst->getMinor()
&& found_patch > ver_inst->getPatch())
{
LL_INFOS() << "Found installed nsis version that is newer" << found_major << "." << found_minor << "." << found_patch << "." << found_build << LL_ENDL;
return;
}
sei.nShow = SW_HIDE;
// Assume that nsis is going to be something like x.x.x, while velopack is x.x.(x+1),
// so there is no point to check build.
LL_INFOS() << "Found NSIS install " << found_major << "." << found_minor << "." << found_patch << "." << found_build << LL_ENDL;
if (!ShellExecuteExW(&sei))
{
LL_WARNS("AppInit") << "Failed to launch NSIS uninstaller, error code: " << GetLastError() << LL_ENDL;
}
});
clear_nsis_links();
LLSD args;
args["VERSION"] = llformat("%d.%d.%d", found_major, found_minor, found_patch);
LLNotificationsUtil::add("FoundLegacyNsisInstallation", args);
#endif
}
@@ -3665,10 +3650,20 @@ void show_release_notes_if_required()
// below. If viewer release notes stop working, might be because that
// LLEventMailDrop got moved out of LLVersionInfo and hasn't yet been
// instantiated.
if (!release_notes_shown && (LLVersionInfo::instance().getChannelAndVersion() != gLastRunVersion)
&& LLVersionInfo::instance().getViewerMaturity() != LLVersionInfo::TEST_VIEWER // don't show Release Notes for the test builds
&& gSavedSettings.getBOOL("UpdaterShowReleaseNotes")
&& !gSavedSettings.getBOOL("FirstLoginThisInstall"))
if (release_notes_shown
|| LLVersionInfo::instance().getChannelAndVersion() == gLastRunVersion
|| gSavedSettings.getBOOL("FirstLoginThisInstall")) // New users don't need to see release notes
{
return;
}
S32 mode = gSavedSettings.getS32("UpdaterShowReleaseNotes");
if (mode == 0)
{
return;
}
if (mode == 2 // Show even for test builds
|| LLVersionInfo::instance().getViewerMaturity() != LLVersionInfo::TEST_VIEWER) // don't show Release Notes for the test builds
{
#if LL_RELEASE_FOR_DOWNLOAD
+95 -25
View File
@@ -405,6 +405,50 @@ static void register_protocol_handler(const std::wstring& protocol,
}
}
void clear_nsis_links()
{
wchar_t path[MAX_PATH];
// 1. The 'start' shortcuts set by nsis would be global, like app shortcut:
// C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Second Life Viewer\Second Life Viewer.lnk
// But it isn't just one link, it's a whole directory that needs to be removed.
if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, path)))
{
std::wstring start_menu_path = path;
std::wstring folder_path = start_menu_path + L"\\" + get_app_name();
std::error_code ec;
std::filesystem::path dir(folder_path);
if (std::filesystem::exists(dir, ec))
{
std::filesystem::remove_all(dir, ec);
if (ec)
{
LL_WARNS("Velopack") << "Failed to remove NSIS start menu directory: "
<< ll_convert_wide_to_string(folder_path) << LL_ENDL;
}
}
}
// 2. Desktop link, also a global one.
// C:\Users\Public\Desktop
if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, NULL, 0, path)))
{
std::wstring desktop_path = path;
std::wstring shortcut_path = desktop_path + L"\\" + get_app_name() + L".lnk";
if (!DeleteFileW(shortcut_path.c_str()))
{
DWORD error = GetLastError();
if (error != ERROR_FILE_NOT_FOUND)
{
LL_WARNS("Velopack") << "Failed to delete NSIS desktop shortcut: "
<< ll_convert_wide_to_string(shortcut_path)
<< " (error: " << error << ")" << LL_ENDL;
}
}
}
}
static void parse_version(const wchar_t* version_str, int& major, int& minor, int& patch, uint64_t& build)
{
major = minor = patch = 0;
@@ -414,7 +458,11 @@ static void parse_version(const wchar_t* version_str, int& major, int& minor, in
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)
bool get_nsis_version(
int& nsis_major,
int& nsis_minor,
int& nsis_patch,
uint64_t& nsis_build)
{
// Test for presence of NSIS viewer registration, then
// attempt to read uninstall info
@@ -439,23 +487,12 @@ bool get_nsis_uninstaller_path(wchar_t* path_buffer, DWORD bufSize, S32 cur_majo
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);
// Make sure it actually exists and not a dead entry.
wchar_t path_buffer[MAX_PATH] = { 0 };
DWORD path_buf_size = sizeof(path_buffer);
LONG rv = RegGetValueW(hkey, nullptr, L"UninstallString", RRF_RT_REG_SZ, &type, path_buffer, &path_buf_size);
RegCloseKey(hkey);
if (rv != ERROR_SUCCESS)
{
@@ -499,10 +536,15 @@ static void register_uninstall_info(const std::wstring& install_dir,
const std::wstring& version)
{
std::wstring app_name_oneword = get_app_name_oneword();
// Clears velopack's recently created 'uninstall' registry entry.
// We are going to use a custom one.
// Note that velopack doesn't know about our custom entry.
std::wstring key_path = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + app_name_oneword;
RegDeleteTreeW(HKEY_CURRENT_USER, key_path.c_str());
// Use a unique key name to avoid conflicts with any existing NSIS-based uninstall info,
// which can cause nly one of the two entries to show up in the Add/Remove Programs list.
// which can cause only one of the two entries to show up in the Add/Remove Programs list.
// The UI will show DisplayName, so the key name itself is not important to be user-friendly.
std::wstring key_path = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Vlpk" + app_name_oneword;
key_path = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Vlpk" + app_name_oneword;
HKEY hkey;
if (RegCreateKeyExW(HKEY_CURRENT_USER, key_path.c_str(), 0, NULL,
@@ -532,7 +574,7 @@ static void register_uninstall_info(const std::wstring& install_dir,
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/";
link_url = L"https://secondlife.com/support/downloads/";
RegSetValueExW(hkey, L"URLUpdateInfo", 0, REG_SZ,
(BYTE*)link_url.c_str(), (DWORD)((link_url.size() + 1) * sizeof(wchar_t)));
@@ -540,7 +582,18 @@ static void register_uninstall_info(const std::wstring& install_dir,
RegSetValueExW(hkey, L"NoModify", 0, REG_DWORD, (BYTE*)&no_modify, sizeof(DWORD));
RegSetValueExW(hkey, L"NoRepair", 0, REG_DWORD, (BYTE*)&no_modify, sizeof(DWORD));
DWORD estimated_size = 120000;
// Format YYYYMMDD
wchar_t dateStr[9];
time_t t = time(NULL);
struct tm tm;
localtime_s(&tm, &t);
wcsftime(dateStr, 9, L"%Y%m%d", &tm);
RegSetValueExW(hkey, L"InstallDate", 0, REG_SZ, (BYTE*)dateStr, (DWORD)((wcslen(dateStr) + 1) * sizeof(wchar_t))); // Let Windows fill in the install date
// 800 MB, inaccurate, but for a rough idea.
// We can check folder size here, but it would take time and
// information is of low importance.
DWORD estimated_size = 800000;
RegSetValueExW(hkey, L"EstimatedSize", 0, REG_DWORD, (BYTE*)&estimated_size, sizeof(DWORD));
RegCloseKey(hkey);
@@ -579,19 +632,31 @@ static void remove_shortcuts(const std::wstring& app_name)
DeleteFileW((desktop_path + L"\\" + app_name + L".lnk").c_str());
}
static void on_first_run(void* p_user_data, const char* app_version)
{
// Velopack first executes 'after install' hook, then writes registry,
// then executes 'on first run' hook.
// As we need to clear velopack's 'uninstall' registry entry and use
// our own, clean it here instead of on_after_install.
std::wstring install_dir = get_install_dir();
std::wstring app_name = get_app_name();
int len = MultiByteToWideChar(CP_UTF8, 0, app_version, -1, NULL, 0);
std::wstring version(len, 0);
MultiByteToWideChar(CP_UTF8, 0, app_version, -1, &version[0], len);
register_uninstall_info(install_dir, app_name, version);
}
static void on_after_install(void* user_data, const char* app_version)
{
std::wstring install_dir = get_install_dir();
std::wstring app_name = get_app_name();
std::wstring exe_path = install_dir + L"\\" + get_viewer_exe_name();
int len = MultiByteToWideChar(CP_UTF8, 0, app_version, -1, NULL, 0);
std::wstring version(len, 0);
MultiByteToWideChar(CP_UTF8, 0, app_version, -1, &version[0], len);
register_protocol_handler(PROTOCOL_SECONDLIFE, L"URL:Second Life", exe_path);
register_protocol_handler(PROTOCOL_GRID_INFO, L"URL:Second Life", exe_path);
register_uninstall_info(install_dir, app_name, version);
create_shortcuts(install_dir, app_name);
}
@@ -620,6 +685,10 @@ static void on_log_message(void* user_data, const char* level, const char* messa
// TODO: Implement protocol handler registration via Launch Services
// TODO: Implement app bundle management
static void on_first_run(void* user_data, const char* app_version)
{
}
static void on_after_install(void* user_data, const char* app_version)
{
// macOS handles protocol registration via Info.plist CFBundleURLTypes
@@ -808,6 +877,7 @@ bool velopack_initialize()
vpkc_app_set_auto_apply_on_startup(false);
#if LL_WINDOWS || LL_DARWIN
vpkc_app_set_hook_first_run(on_first_run);
vpkc_app_set_hook_after_install(on_after_install);
vpkc_app_set_hook_before_uninstall(on_before_uninstall);
#endif
+6 -1
View File
@@ -46,7 +46,12 @@ void velopack_set_progress_callback(std::function<void(int)> callback);
void velopack_cleanup();
#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);
void clear_nsis_links();
bool get_nsis_version(
int& nsis_major,
int& nsis_minor,
int& nsis_patch,
uint64_t& nsis_build);
#endif
#endif // LL_VELOPACK
@@ -202,17 +202,12 @@ No tutorial is currently available.
<notification
icon="alertmodal.tga"
name="PromptRemoveNsisInstallation"
name="FoundLegacyNsisInstallation"
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
ignoretext="Ask to remove legacy installation"
name="okcancelignore"
notext="Cancel"
yestext="Uninstall"/>
[APP_NAME] found an installation of an older version [VERSION]. To uninstall the older version, please follow [https://community.secondlife.com/knowledgebase/english/how-to-uninstall-and-reinstall-second-life-r524 this manual].
<usetemplate
name="okbutton"
yestext="OK"/>
</notification>
<notification
+6 -15
View File
@@ -658,21 +658,6 @@ class Windows_x86_64_Manifest(ViewerManifest):
# '*.tar.xz')))
# </FS:Ansariel>
# <FS:Ansariel> Remove VMP
#with self.prefix(src=os.path.join(pkgdir, "VMP")):
# include the compiled launcher scripts so that it gets included in the file_list
# self.path('SLVersionChecker.exe')
#with self.prefix(dst="vmp_icons"):
# with self.prefix(src=self.icon_path()):
# self.path("secondlife.ico")
#VMP Tkinter icons
# with self.prefix(src="vmp_icons"):
# self.path("*.png")
# self.path("*.gif")
# </FS:Ansariel> Remove VMP
# Plugin host application
self.path2basename(os.path.join(os.pardir,
'llplugin', 'slplugin', self.args['configuration']),
@@ -2002,6 +1987,12 @@ class Darwin_x86_64_Manifest(ViewerManifest):
if self.args.get('velopack', 'OFF') == 'ON':
self.velopack_package_finish()
# 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.