Skip to main content
Glama
installer.nsi12.9 kB
# Medplum Agent Installer Builder # For use with NSIS 3.0+ # See: https://nsis.sourceforge.io/ !define COMPANY_NAME "Medplum" !define APP_NAME "Medplum Agent" !define BASE_SERVICE_NAME "MedplumAgent" !define SERVICE_NAME "${BASE_SERVICE_NAME}_$%MEDPLUM_VERSION%-$%MEDPLUM_GIT_SHORTHASH%" !define SERVICE_DESCRIPTION "Securely connects local devices to ${COMPANY_NAME} cloud" !define SERVICE_FILE_NAME "medplum-agent-$%MEDPLUM_VERSION%-$%MEDPLUM_GIT_SHORTHASH%-win64.exe" !define INSTALLER_FILE_NAME "medplum-agent-installer-$%MEDPLUM_VERSION%-$%MEDPLUM_GIT_SHORTHASH%.exe" !define PRODUCT_VERSION "$%MEDPLUM_VERSION%.0" !define DEFAULT_BASE_URL "https://api.medplum.com/" Name "${APP_NAME}" OutFile "${INSTALLER_FILE_NAME}" VIProductVersion "${PRODUCT_VERSION}" VIAddVersionKey ProductName "${APP_NAME}" VIAddVersionKey Comments "${APP_NAME}" VIAddVersionKey CompanyName "${COMPANY_NAME}" VIAddVersionKey LegalCopyright "${COMPANY_NAME}" VIAddVersionKey FileDescription "${APP_NAME}" VIAddVersionKey FileVersion 1 VIAddVersionKey ProductVersion 1 VIAddVersionKey InternalName "${APP_NAME}" VIAddVersionKey LegalTrademarks "${COMPANY_NAME}" VIAddVersionKey OriginalFilename "${INSTALLER_FILE_NAME}" InstallDir "$PROGRAMFILES64\${APP_NAME}" !include "nsDialogs.nsh" !include "x64.nsh" !include "LogicLib.nsh" RequestExecutionLevel admin Var WelcomeDialog Var WelcomeLabel Var alreadyInstalled Var alreadyInstalledThisVersion Var foundPropertiesFile Var baseUrl Var clientId Var clientSecret Var agentId # Vars for Section StopAndDeleteOldMedplumServices Var ServicesList Var WorkingList Var TempStr Var LineLen Var TempLen Var CurrentLen Var CurrentServiceName # The onInit handler is called when the installer is nearly finished initializing. # See: https://nsis.sourceforge.io/Reference/.onInit Function .onInit ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${BASE_SERVICE_NAME}" "DisplayName" ${If} $0 != "" StrCpy $alreadyInstalled 1 ${Else} StrCpy $alreadyInstalled 0 ${EndIf} ReadRegStr $0 HKLM "SYSTEM\CurrentControlSet\Services\${SERVICE_NAME}" "ImagePath" ${If} $0 != "" StrCpy $alreadyInstalledThisVersion 1 ${Else} StrCpy $alreadyInstalledThisVersion 0 ${EndIf} ${If} ${FileExists} "$INSTDIR\agent.properties" StrCpy $foundPropertiesFile 1 ${Else} StrCpy $foundPropertiesFile 0 ${EndIf} # Check if already installed and properties file not found ${If} $alreadyInstalled == 1 ${AndIf} $foundPropertiesFile == 0 MessageBox MB_ICONSTOP "The currently installed version is too old and needs to be uninstalled manually before proceeding." Quit ${EndIf} FunctionEnd Page custom WelcomePage Page custom InputPage InputPageLeave Page instfiles # The WelcomePage is a simple static screen that displays a friendly message. Function WelcomePage nsDialogs::Create 1018 Pop $WelcomeDialog ${If} $WelcomeDialog == error Abort ${EndIf} ${NSD_CreateLabel} 0 0 100% 50u "Welcome to the ${APP_NAME} Installer!$\r$\n$\r$\nClick next to continue." Pop $WelcomeLabel nsDialogs::Show FunctionEnd # The InputPage captures all of the user input for the agent. Function InputPage ${If} $alreadyInstalled == 1 Abort ; This skips the page ${EndIf} nsDialogs::Create 1018 Pop $0 StrCpy $baseUrl "${DEFAULT_BASE_URL}" ${NSD_CreateLabel} 0 0 30% 12u "Base URL:" Pop $R0 ${NSD_CreateText} 35% 0 65% 12u $baseUrl Pop $R1 ${NSD_CreateLabel} 0 15u 30% 12u "Client ID:" Pop $R2 ${NSD_CreateText} 35% 15u 65% 12u $clientId Pop $R3 ${NSD_CreateLabel} 0 30u 30% 12u "Client Secret:" Pop $R4 ${NSD_CreateText} 35% 30u 65% 12u $clientSecret Pop $R5 ${NSD_CreateLabel} 0 45u 30% 12u "Agent ID:" Pop $R6 ${NSD_CreateText} 35% 45u 65% 12u $agentId Pop $R7 ${NSD_SetFocus} $R3 nsDialogs::Show FunctionEnd Function InputPageLeave ${NSD_GetText} $R1 $baseUrl ${NSD_GetText} $R3 $clientId ${NSD_GetText} $R5 $clientSecret ${NSD_GetText} $R7 $agentId StrCmp $baseUrl "" inputError StrCmp $clientId "" inputError StrCmp $clientSecret "" inputError StrCmp $agentId "" inputError Goto inputOK inputError: MessageBox MB_OK|MB_ICONEXCLAMATION "Please fill in all required fields." Abort inputOK: FunctionEnd # Main installation entry point. Section DetailPrint "${APP_NAME}" SetOutPath "$INSTDIR" ${If} $alreadyInstalledThisVersion == 1 DetailPrint "This version is already installed on this system." Abort ${ElseIf} $alreadyInstalled == 1 Call UpgradeApp ${Else} Call InstallApp ${EndIf} SectionEnd # Upgrade an existing installation. # This only copies files, and restarts the Windows Service. # It does not modify the existing configuration settings. Function UpgradeApp # Copy the new files to the installation directory # We temporarily set overwrite to "ifdiff" so that we don't try to overwrite files that may already exist from a previous installation # This should prevent us from trying to overwrite shawl which could still be running, but shouldn't be different if it's the same version SetOverwrite ifdiff File dist\shawl-v1.5.0-legal.txt File dist\shawl-v1.5.0-win64.exe File dist\${SERVICE_FILE_NAME} File README.md # We set overwrite back to the default value, "on" SetOverwrite on # Create the service DetailPrint "Creating service..." ExecWait "shawl-v1.5.0-win64.exe add --name $\"${SERVICE_NAME}$\" --log-as $\"${SERVICE_NAME}$\" --cwd $\"$INSTDIR$\" -- $\"$INSTDIR\${SERVICE_FILE_NAME}$\"" $1 DetailPrint "Exit code $1" # Set service display name DetailPrint "Setting service display name..." ExecWait "sc.exe config $\"${SERVICE_NAME}$\" displayname= $\"${APP_NAME}$\"" $1 DetailPrint "Exit code $1" # Set service description DetailPrint "Setting service description..." ExecWait "sc.exe description $\"${SERVICE_NAME}$\" $\"${SERVICE_DESCRIPTION}$\"" $1 DetailPrint "Exit code $1" # Set service to start automatically DetailPrint "Setting service to start automatically..." ExecWait "sc.exe config $\"${SERVICE_NAME}$\" start= auto" $1 DetailPrint "Exit code $1" # Set service to restart on failure DetailPrint "Setting service to restart on failure..." ExecWait "sc.exe failure $\"${SERVICE_NAME}$\" reset= 0 actions= restart/0/restart/0/restart/0" DetailPrint "Exit code $1" # Check if there is an upgrade manifest already, if not, add one without a callback # This is to activate the "maybeFinalizeUpgrade" path in the agent, which will make sure we delete the upgrade manifest # Which we use as a signal to continue after the agent has either bound or started its loop to attempt to bind to all the server ports ${If} ${FileExists} "$INSTDIR\upgrade.json" DetailPrint "upgrade.json already exists, skipping creation" ${Else} DetailPrint "Creating upgrade.json file" # Create the file with JSON content FileOpen $1 "$INSTDIR\upgrade.json" w FileWrite $1 '{ "previousVersion": "UNKNOWN", "targetVersion": "$%MEDPLUM_VERSION%", "callback": null }' FileClose $1 ${EndIf} # Start the service DetailPrint "Starting service..." ExecWait "sc.exe start $\"${SERVICE_NAME}$\"" $1 DetailPrint "Exit code $1" # Check if service attempting to bind to ports # The agent should be attempting to bind to the ports, or already bound to ports if old service not running ${Do} ${If} ${FileExists} "$INSTDIR\upgrade.json" DetailPrint "Waiting for upgrade.json to be removed..." Sleep 500 ${Else} DetailPrint "upgrade.json removed, continuing..." ${Break} ${EndIf} ${Loop} DetailPrint "Stopping and deleting old Medplum Agent services..." ExecWait "$\"$INSTDIR\${SERVICE_FILE_NAME}$\" --remove-old-services" $1 DetailPrint "Exit code $1" DetailPrint "Writing uninstaller for upgraded version..." WriteUninstaller "$INSTDIR\uninstall.exe" FunctionEnd # Do the actual installation. # Install all of the files. # Install the Windows Service. Function InstallApp # Show architecture !if "${NSIS_PTR_SIZE}" >= 8 DetailPrint "64-bit installer" !else ${If} ${RunningX64} DetailPrint "32-bit installer on a 64-bit OS" ${Else} DetailPrint "32-bit installer on a 32-bit OS" ${EndIf} !endif # Print user input DetailPrint "Base URL: $baseUrl" DetailPrint "Client ID: $clientId" DetailPrint "Client Secret: $clientSecret" DetailPrint "Agent ID: $agentId" # Copy the service files to the root directory File dist\shawl-v1.5.0-legal.txt File dist\shawl-v1.5.0-win64.exe File dist\${SERVICE_FILE_NAME} File README.md # Create the agent.properties config file FileOpen $9 agent.properties w FileWrite $9 "baseUrl=$baseUrl$\r$\n" FileWrite $9 "clientId=$clientId$\r$\n" FileWrite $9 "clientSecret=$clientSecret$\r$\n" FileWrite $9 "agentId=$agentId$\r$\n" FileClose $9 # Create the service DetailPrint "Creating service..." ExecWait "shawl-v1.5.0-win64.exe add --name $\"${SERVICE_NAME}$\" --log-as $\"${SERVICE_NAME}$\" --cwd $\"$INSTDIR$\" -- $\"$INSTDIR\${SERVICE_FILE_NAME}$\"" $1 DetailPrint "Exit code $1" # Set service display name DetailPrint "Setting service display name..." ExecWait "sc.exe config $\"${SERVICE_NAME}$\" displayname= $\"${APP_NAME}$\"" $1 DetailPrint "Exit code $1" # Set service description DetailPrint "Setting service description..." ExecWait "sc.exe description $\"${SERVICE_NAME}$\" $\"${SERVICE_DESCRIPTION}$\"" $1 DetailPrint "Exit code $1" # Set service to start automatically DetailPrint "Setting service to start automatically..." ExecWait "sc.exe config $\"${SERVICE_NAME}$\" start= auto" $1 DetailPrint "Exit code $1" # Set service to restart on failure DetailPrint "Setting service to restart on failure..." ExecWait "sc.exe failure $\"${SERVICE_NAME}$\" reset= 0 actions= restart/0/restart/0/restart/0" DetailPrint "Exit code $1" # Start the service DetailPrint "Starting service..." ExecWait "sc.exe start $\"${SERVICE_NAME}$\"" $1 DetailPrint "Exit code $1" # Create the uninstaller DetailPrint "Creating the uninstaller..." SetOutPath $INSTDIR WriteUninstaller "$INSTDIR\uninstall.exe" # Register the uninstaller DetailPrint "Registering the uninstaller..." WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${BASE_SERVICE_NAME}" "DisplayName" "${APP_NAME}" WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${BASE_SERVICE_NAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${BASE_SERVICE_NAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" DetailPrint "Uninstaller complete" # Create Start menu shortcuts DetailPrint "$SMPROGRAMS\${APP_NAME}\${APP_NAME} Uninstall.lnk" CreateDirectory "$SMPROGRAMS\${APP_NAME}" CreateShortCut "$SMPROGRAMS\${APP_NAME}\${APP_NAME} Uninstall.lnk" "$INSTDIR\uninstall.exe" FunctionEnd # Start the uninstaller Section Uninstall DetailPrint "Stopping and deleting all old Medplum Agent services..." ExecWait "$\"$INSTDIR\${SERVICE_FILE_NAME}$\" --remove-old-services --all" $1 DetailPrint "Exit code $1" # Get out of the service directory so we can delete it SetOutPath "$PROGRAMFILES64" # Uninstall the Start menu shortcuts RMDir /r /REBOOTOK "$SMPROGRAMS\${APP_NAME}" # Delete the files RMDir /r /REBOOTOK "$INSTDIR" # Unregister the program DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${BASE_SERVICE_NAME}" SectionEnd # Check if we should skip signing... this can be set via /D cli arg !ifndef SKIP_SIGNING # Sign the installer and uninstaller # Keep in mind that you must append = 0 at !finalize and !uninstfinalize. # That will stop running both in parallel. !finalize 'java -jar jsign-5.0.jar --storetype DIGICERTONE --storepass "$%SM_API_KEY%|$%SM_CLIENT_CERT_FILE%|$%SM_CLIENT_CERT_PASSWORD%" --alias "$%SM_CERT_ALIAS%" "%1"' = 0 !uninstfinalize 'java -jar jsign-5.0.jar --storetype DIGICERTONE --storepass "$%SM_API_KEY%|$%SM_CLIENT_CERT_FILE%|$%SM_CLIENT_CERT_PASSWORD%" --alias "$%SM_CERT_ALIAS%" "%1"' = 0 !endif

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/medplum/medplum'

If you have feedback or need assistance with the MCP directory API, please join our Discord server