diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml index da0fdbe..397e0f3 100644 --- a/.github/workflows/image.yml +++ b/.github/workflows/image.yml @@ -47,7 +47,7 @@ jobs: - name: Download Purple HATS Backend shell: pwsh run: | - $PHbackendUrl = "https://github.com/GovTechSG/purple-hats/releases/download/${{ vars.BE_TAG }}/purple-hats-portable-windows.zip" + $PHbackendUrl = "https://github.com/GovTechSG/purple-hats/releases/download/0.9.36/purple-hats-portable-windows.zip" $BEdestinationPath = "${{github.workspace}}\PHLatest.zip" $BEextractPath = "D:\a\Purple HATS Backend" Invoke-WebRequest -Uri $PHbackendUrl -OutFile $BEdestinationPath @@ -128,7 +128,7 @@ jobs: npm run make-mac env: CI: "" - BE_TAG: "${{ vars.BE_TAG }}" + BE_TAG: "0.9.36" APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} diff --git a/package.json b/package.json index 374d609..9f8abf0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "purplehatsdesktop", "productName": "Purple HATS", - "version": "0.9.35", + "version": "0.9.40-transition", "private": true, "dependencies": { "axios": "^1.6.0", diff --git a/public/electron/constants.js b/public/electron/constants.js index f588649..2a09ce7 100644 --- a/public/electron/constants.js +++ b/public/electron/constants.js @@ -22,8 +22,8 @@ const allReleasesUrl = "https://api.github.com/repos/GovTechSG/purple-hats/relea const frontendReleaseUrl = os.platform() === "win32" - ? "https://github.com/GovTechSG/purple-hats-desktop/releases/latest/download/purple-hats-desktop-windows.zip" - : "https://github.com/GovTechSG/purple-hats-desktop/releases/latest/download/purple-hats-desktop-macos.zip"; + ? "https://github.com/GovTechSG/purple-a11y-desktop/releases/latest/download/purple-a11y-desktop-windows.zip" + : "https://github.com/GovTechSG/purple-a11y-desktop/releases/latest/download/purple-a11y-desktop-macos.zip"; const backendPath = path.join(appPath, "Purple HATS Backend"); const frontendPath = path.join(appPath, "Purple HATS Frontend"); diff --git a/public/electron/main.js b/public/electron/main.js index 9cd7e34..89fe577 100644 --- a/public/electron/main.js +++ b/public/electron/main.js @@ -60,7 +60,7 @@ app.on("ready", async () => { }), }); - const { data: releaseInfo } = await axiosInstance.get('https://govtechsg.github.io/purple-hats-desktop/latest-release.json') + const { data: releaseInfo } = await axiosInstance.get('https://govtechsg.github.io/purple-a11y-desktop/latest-release.json') .catch((e) => { console.log("Unable to get release info"); return { data: undefined }; diff --git a/public/electron/updateManager.js b/public/electron/updateManager.js index 22f4097..11eb288 100644 --- a/public/electron/updateManager.js +++ b/public/electron/updateManager.js @@ -81,8 +81,14 @@ const hashPrepackage = async (prepackagePath) => { // unzip backend zip for mac const unzipBackendAndCleanUp = async (zipPath=phZipPath) => { - let unzipCommand = `mkdir -p '${backendPath}' && tar -xf '${zipPath}' -C '${backendPath}' && - cd '${backendPath}' && + const tempBackendPath = path.join( + os.homedir(), + "Library", + "Application Support", + "Purple HATS", + "Purple HATS Backend"); + let unzipCommand = `mkdir -p '${tempBackendPath}' && tar -xf '${zipPath}' -C '${tempBackendPath}' && + cd '${tempBackendPath}' && './hats_shell.sh' echo "Initialise" `; @@ -91,7 +97,7 @@ const unzipBackendAndCleanUp = async (zipPath=phZipPath) => { } }; -const getLatestFrontendVersion = async (latestRelease, latestPreRelease) => { +const getLatestFrontendVersion = (latestRelease, latestPreRelease) => { try { let verToCompare; if (isLabMode) { @@ -102,10 +108,7 @@ const getLatestFrontendVersion = async (latestRelease, latestPreRelease) => { } else { verToCompare = latestRelease; } - if (versionComparator(appFrontendVer, verToCompare) === -1) { - return verToCompare; - } - return undefined; // no need for update + return verToCompare; } catch (e) { console.log(`Unable to check latest frontend version, skipping\n${e.toString()}`); return undefined; @@ -118,12 +121,16 @@ const getLatestFrontendVersion = async (latestRelease, latestPreRelease) => { */ const downloadAndUnzipFrontendWindows = async (tag=undefined) => { const downloadUrl = tag - ? `https://github.com/GovTechSG/purple-hats-desktop/releases/download/${tag}/purple-hats-desktop-windows.zip` + ? `https://github.com/GovTechSG/purple-a11y-desktop/releases/download/${tag}/purple-a11y-desktop-windows.zip` : frontendReleaseUrl; + const tempResultsPath = path.join(process.env.APPDATA, "Purple A11y"); const shellScript = ` $webClient = New-Object System.Net.WebClient try { - $webClient.DownloadFile("${downloadUrl}", "${resultsPath}\\purple-hats-desktop-windows.zip") + If (!(Test-Path -Path "${tempResultsPath}")) { + New-Item -ItemType Directory -Path "${tempResultsPath}" + } + $webClient.DownloadFile("${downloadUrl}", "${tempResultsPath}\\purple-a11y-desktop-windows.zip") } catch { Write-Host "Error: Unable to download frontend" throw "Unable to download frontend" @@ -131,7 +138,7 @@ const downloadAndUnzipFrontendWindows = async (tag=undefined) => { } try { - Expand-Archive -Path "${resultsPath}\\purple-hats-desktop-windows.zip" -DestinationPath "${resultsPath}\\purple-hats-desktop-windows" -Force + Expand-Archive -Path "${tempResultsPath}\\purple-a11y-desktop-windows.zip" -DestinationPath "${tempResultsPath}\\purple-a11y-desktop-windows" -Force } catch { Write-Host "Error: Unable to unzip frontend" throw "Unable to unzip frontend" @@ -143,7 +150,7 @@ const downloadAndUnzipFrontendWindows = async (tag=undefined) => { currentChildProcess = ps; ps.stdout.on("data", (data) => { - silentLogger.log(data.toString()); + silentLogger.debug(data.toString()); }); // Log any errors from the PowerShell script @@ -166,26 +173,51 @@ const downloadAndUnzipFrontendWindows = async (tag=undefined) => { }); }; +/** + * Spawns a Shell Command process to make Purple A11y directory and copy over userData.txt + */ +const createNewAppDir = async (appDirPath) => { + const oldUserDataPath = path.join( + os.homedir(), + "Library", + "Application Support", + "Purple HATS", + "userData.txt", + ); + const command = ` + mkdir -p '${appDirPath}' && + [ -f '${oldUserDataPath}' ] && cp '${oldUserDataPath}' '${appDirPath}' + ` + + await execCommand(command); +}; + /** * Spawns a Shell Command process to download and unzip the frontend */ const downloadAndUnzipFrontendMac = async (tag=undefined) => { const downloadUrl = tag - ? `https://github.com/GovTechSG/purple-hats-desktop/releases/download/${tag}/purple-hats-desktop-macos.zip` + ? `https://github.com/GovTechSG/purple-a11y-desktop/releases/download/${tag}/purple-a11y-desktop-macos.zip` : frontendReleaseUrl; + const tempResultsPath = path.join( + os.homedir(), + "Library", + "Application Support", + "Purple A11y" + ) + + await createNewAppDir(tempResultsPath); + const command = ` - curl -L '${downloadUrl}' -o '${resultsPath}/purple-hats-desktop-mac.zip' && - mv '${macOSExecutablePath}' '${path.join( - macOSExecutablePath, - ".." - )}/Purple Hats Old.app' && - ditto -xk '${resultsPath}/purple-hats-desktop-mac.zip' '${path.join( + curl -L '${downloadUrl}' -o '${tempResultsPath}/purple-a11y-desktop-mac.zip' && + ditto -xk '${tempResultsPath}/purple-a11y-desktop-mac.zip' '${path.join( macOSExecutablePath, ".." )}' && - rm '${resultsPath}/purple-hats-desktop-mac.zip' && - rm -rf '${path.join(macOSExecutablePath, "..")}/Purple Hats Old.app' && - xattr -rd com.apple.quarantine '${path.join(macOSExecutablePath, "..")}/Purple HATS.app' `; + rm '${tempResultsPath}/purple-a11y-desktop-mac.zip' && + xattr -rd com.apple.quarantine '${path.join(macOSExecutablePath, "..")}/Purple A11y.app' && + open '${path.join(macOSExecutablePath, "..")}/Purple A11y.app' && + rm -rf '${macOSExecutablePath}'`; await execCommand(command); @@ -197,7 +229,13 @@ const downloadAndUnzipFrontendMac = async (tag=undefined) => { * @returns {Promise} true if the installer executable was launched successfully, false otherwise */ const spawnScriptToLaunchInstaller = () => { - const shellScript = `Start-Process -FilePath "${installerExePath}"`; + const tempResultsPath = path.join(process.env.APPDATA, "Purple A11y"); + const tempInstallerExePath = path.join( + tempResultsPath, + "purple-a11y-desktop-windows", + "Purple-A11y-Setup.exe" + ); + const shellScript = `Start-Process -FilePath "${tempInstallerExePath}"`; return new Promise((resolve, reject) => { const ps = spawn("powershell.exe", ["-Command", shellScript]); @@ -271,7 +309,7 @@ const downloadAndUnzipBackendWindows = async (tag=undefined) => { }; const downloadBackend = async (tag=undefined) => { - const downloadUrl = `https://github.com/GovTechSG/purple-hats/releases/download/${tag}/purple-hats-portable-mac.zip`; + const downloadUrl = `https://github.com/GovTechSG/purple-a11y/releases/download/${tag}/purple-a11y-portable-mac.zip`; const command = `curl '${downloadUrl}' -o '${phZipPath}' -L && rm -rf '${backendPath}' && mkdir '${backendPath}'`; return async () => await execCommand(command); @@ -307,7 +345,8 @@ const run = async (updaterEventEmitter, latestRelease, latestPreRelease) => { const backendExists = fs.existsSync(backendPath); const phZipExists = fs.existsSync(phZipPath); - const toUpdateFrontendVer = await getLatestFrontendVersion(latestRelease, latestPreRelease); + + const toUpdateFrontendVer = getLatestFrontendVersion(latestRelease, latestPreRelease); // Auto updates via installer is only applicable for Windows // Auto updates for backend on Windows will be done via a powershell script due to %ProgramFiles% permission @@ -342,7 +381,8 @@ const run = async (updaterEventEmitter, latestRelease, latestPreRelease) => { const isInstallerScriptLaunched = await spawnScriptToLaunchInstaller(); if (isInstallerScriptLaunched) { - writeUserDetailsToFile({ firstLaunchOnUpdate: true }); + // TODO: Should not need this, basically fresh install because userData.txt should not exist + // writeUserDetailsToFile({ firstLaunchOnUpdate: true }); updaterEventEmitter.emit("installerLaunched"); } } @@ -351,78 +391,28 @@ const run = async (updaterEventEmitter, latestRelease, latestPreRelease) => { } } } else if (!backendExists) { + // TODO: should not enter here + console.log("Should not enter backend does not exist block") updaterEventEmitter.emit('settingUp'); // Trigger download for backend via Github if backend does not exist + // TODO: Did not work on this for updater (old impl) await downloadAndUnzipBackendWindows(appFrontendVer); } } else { // user is on mac - if (toUpdateFrontendVer) { - const userResponse = new Promise((resolve) => { - updaterEventEmitter.emit("promptFrontendUpdate", resolve); - }); + updaterEventEmitter.emit("updatingFrontend"); + // Relaunch the app with new binaries if the frontend update is successful + // If unsuccessful, the app will be launched with existing frontend + try { + // downloads and opens the new frontend + await downloadAndUnzipFrontendMac(toUpdateFrontendVer); - const proceedUpdate = await userResponse; - - if (proceedUpdate) { - updaterEventEmitter.emit("updatingFrontend"); - - // Relaunch the app with new binaries if the frontend update is successful - // If unsuccessful, the app will be launched with existing frontend - try { - await downloadAndUnzipFrontendMac(toUpdateFrontendVer); - currentChildProcess = null; - - if (await validateZipFile(macOSPrepackageBackend)) { - processesToRun.push(hashAndSaveZip(macOSPrepackageBackend)); - processesToRun.push(await unzipBackendAndCleanUp(macOSPrepackageBackend)); - } else { - processesToRun.push( - await downloadBackend(toUpdateFrontendVer), - hashAndSaveZip(phZipPath), - await unzipBackendAndCleanUp() - ); - } + currentChildProcess = null; - writeUserDetailsToFile({ firstLaunchOnUpdate: true }); - processesToRun.push(() => updaterEventEmitter.emit("restartTriggered")); - } catch (e) { - silentLogger.error(e.toString()); - } - } - } else if (!backendExists) { - updaterEventEmitter.emit('settingUp'); - if (await validateZipFile(macOSPrepackageBackend)) { - // Trigger an unzip from Resources folder if backend does not exist or backend is older - processesToRun.push( - await unzipBackendAndCleanUp(macOSPrepackageBackend), - hashAndSaveZip(macOSPrepackageBackend) - ); - } else { - processesToRun.push( - await downloadBackend(appFrontendVer), - hashAndSaveZip(phZipPath), - await unzipBackendAndCleanUp() - ); - } - } else if (backendExists && await validateZipFile(macOSPrepackageBackend)) { - // compare zip file hash to determine whether to unzip - // current hash of prepackage - const currHash = await hashPrepackage(macOSPrepackageBackend); - if (fs.existsSync(hashPath)) { - // check if match - const hash = fs.readFileSync(hashPath, "utf-8"); // stored hash - // compare - if (hash === currHash) { - // dont unzip - return; - } - } - processesToRun.push(() => updaterEventEmitter.emit('settingUp')); - // unzip - processesToRun.push(await unzipBackendAndCleanUp(macOSPrepackageBackend)); - // write hash - processesToRun.push(() => fs.writeFileSync(hashPath, currHash)); + // exit the old (current) app + processesToRun.push(() => updaterEventEmitter.emit("installerLaunched")); + } catch (e) { + silentLogger.error(e.toString()); } for (const proc of processesToRun) { diff --git a/src/MainWindow/HomePage/AboutModal.jsx b/src/MainWindow/HomePage/AboutModal.jsx index 269c461..b523aef 100644 --- a/src/MainWindow/HomePage/AboutModal.jsx +++ b/src/MainWindow/HomePage/AboutModal.jsx @@ -141,7 +141,9 @@ const AboutModal = ({ const toCompare = isLabMode ? latestVerForLab : latestVer; const isNeedUpdate = versionComparator(appVersion, toCompare) === -1; - if (isNeedUpdate) { + if (toCompare === "0.9.40" && appVersion === "0.9.40-transition") { + setToUpdateVer(toCompare); + } else if (isNeedUpdate) { setToUpdateVer(toCompare); } else { setToUpdateVer(undefined);