diff --git a/.github/dependabot.yml b/.github/dependabot.yml index cbfbb4baa..2ca21f95a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,17 +1,17 @@ version: 2 updates: - - package-ecosystem: github-actions - directory: "/" - schedule: - interval: daily - target-branch: main - - package-ecosystem: npm - directory: "/" - schedule: - interval: daily - target-branch: main - - package-ecosystem: pip - directory: "/" - schedule: - interval: daily - target-branch: main + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + target-branch: main + - package-ecosystem: npm + directory: "/" + schedule: + interval: daily + target-branch: main + - package-ecosystem: pip + directory: "/" + schedule: + interval: daily + target-branch: main diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index a4fdb1848..67dc12987 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -3,23 +3,23 @@ name: Validate and process static files on: [pull_request, push] jobs: - static: - runs-on: ubuntu-latest + static: + runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - cache: npm - - uses: actions/setup-python@v5 - with: - python-version: '3.11' - cache: pip + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + cache: npm + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: pip - - run: sudo apt-get update - - run: sudo apt-get -y install libxml2-utils yajl-tools moreutils zopfli - - run: npm ci --ignore-scripts - - run: 'pip install --require-hashes --only-binary :all: -r requirements.txt' + - run: sudo apt-get update + - run: sudo apt-get -y install libxml2-utils yajl-tools moreutils zopfli + - run: npm ci --ignore-scripts + - run: "pip install --require-hashes --only-binary :all: -r requirements.txt" - - name: process static - run: ./process-static + - name: process static + run: ./process-static diff --git a/eslint.config.js b/eslint.config.js index e279b849d..f437019fe 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,18 +6,18 @@ export default [ { languageOptions: { globals: { - ...globals.browser + ...globals.browser, }, ecmaVersion: 2022, - sourceType: "module" + sourceType: "module", }, rules: { indent: ["error", 4], "linebreak-style": ["error", "unix"], quotes: ["error", "double"], semi: ["error", "always"], - "no-unused-vars": ["error", {caughtErrors: "none"}], - "no-var": ["error"] - } - } + "no-unused-vars": ["error", { caughtErrors: "none" }], + "no-var": ["error"], + }, + }, ]; diff --git a/static/js/redirect.js b/static/js/redirect.js index 1f8edeadb..5eb94db94 100644 --- a/static/js/redirect.js +++ b/static/js/redirect.js @@ -18,15 +18,23 @@ const redirects = new Map([ ["/usage#default-connections", "/faq#default-connections"], ["/usage#sandboxed-google-play-esim", "/usage#esim-support"], ["/usage#sandboxed-play-services", "/usage#sandboxed-google-play"], - ["/usage#sandboxed-play-services-installation", "/usage#sandboxed-google-play-installation"], - ["/usage#sandboxed-play-services-limitations", "/usage#sandboxed-google-play-limitations"], + [ + "/usage#sandboxed-play-services-installation", + "/usage#sandboxed-google-play-installation", + ], + [ + "/usage#sandboxed-play-services-limitations", + "/usage#sandboxed-google-play-limitations", + ], ["/usage#google-camera", "/usage#pixel-camera"], ["/faq#dns", "/faq#custom-dns"], ["/faq#when-devices", "/faq#future-devices"], - - ["/features#usb-c-port-control", "/features#usb-c-port-and-pogo-pins-control"], + [ + "/features#usb-c-port-control", + "/features#usb-c-port-and-pogo-pins-control", + ], ["/hiring#qualitifations", "/hiring#qualifications"], @@ -55,28 +63,55 @@ const redirects = new Map([ ["/releases#blueline-beta", "/faq#legacy-devices"], // legacy servers - ["/articles/grapheneos-servers#apps.grapheneos.org", "/articles/grapheneos-servers#releases.grapheneos.org"], - ["/articles/grapheneos-servers#time.grapheneos.org", "/articles/grapheneos-servers#grapheneos.network"], + [ + "/articles/grapheneos-servers#apps.grapheneos.org", + "/articles/grapheneos-servers#releases.grapheneos.org", + ], + [ + "/articles/grapheneos-servers#time.grapheneos.org", + "/articles/grapheneos-servers#grapheneos.network", + ], // preserve links to CLI install guide from when it was /install ["/install/#prerequisites", "/install/cli#prerequisites"], ["/install/#enabling-oem-unlocking", "/install/cli#enabling-oem-unlocking"], ["/install/#opening-terminal", "/install/cli#opening-terminal"], ["/install/#obtaining-fastboot", "/install/cli#obtaining-fastboot"], - ["/install/#standalone-platform-tools", "/install/cli#standalone-platform-tools"], - ["/install/#checking-fastboot-version", "/install/cli#checking-fastboot-version"], + [ + "/install/#standalone-platform-tools", + "/install/cli#standalone-platform-tools", + ], + [ + "/install/#checking-fastboot-version", + "/install/cli#checking-fastboot-version", + ], ["/install/#fastboot-as-non-root", "/install/cli#flashing-as-non-root"], ["/install/#connecting-phone", "/install/cli#connecting-phone"], - ["/install/#unlocking-the-bootloader", "/install/cli#unlocking-the-bootloader"], + [ + "/install/#unlocking-the-bootloader", + "/install/cli#unlocking-the-bootloader", + ], ["/install/#obtaining-signify", "/install/cli#obtaining-openssh"], - ["/install/#obtaining-factory-images", "/install/cli#obtaining-factory-images"], - ["/install/#flashing-factory-images", "/install/cli#flashing-factory-images"], + [ + "/install/#obtaining-factory-images", + "/install/cli#obtaining-factory-images", + ], + [ + "/install/#flashing-factory-images", + "/install/cli#flashing-factory-images", + ], ["/install/#troubleshooting", "/install/cli#troubleshooting"], ["/install/#locking-the-bootloader", "/install/cli#locking-the-bootloader"], ["/install/#post-installation", "/install/cli#post-installation"], ["/install/#booting", "/install/cli#booting"], - ["/install/#disabling-oem-unlocking", "/install/cli#disabling-oem-unlocking"], - ["/install/#replacing-grapheneos-with-the-stock-os", "/install/cli#replacing-grapheneos-with-the-stock-os"], + [ + "/install/#disabling-oem-unlocking", + "/install/cli#disabling-oem-unlocking", + ], + [ + "/install/#replacing-grapheneos-with-the-stock-os", + "/install/cli#replacing-grapheneos-with-the-stock-os", + ], ["/install/#further-information", "/install/cli#further-information"], ["/install/web#connecting-phone", "/install/web#connecting-device"], ["/install/cli#connecting-phone", "/install/cli#connecting-device"], @@ -84,7 +119,9 @@ const redirects = new Map([ function handleHash() { if (window.location.hash) { - const redirect = redirects.get(window.location.pathname + window.location.hash); + const redirect = redirects.get( + window.location.pathname + window.location.hash, + ); if (redirect) { window.location.replace(redirect); } diff --git a/static/js/releases.js b/static/js/releases.js index ec69275c1..465ebeff5 100644 --- a/static/js/releases.js +++ b/static/js/releases.js @@ -1,7 +1,29 @@ // @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT const baseUrl = "https://releases.grapheneos.org/"; -const devices = ["comet", "komodo", "caiman", "tokay", "akita", "husky", "shiba", "felix", "tangorpro", "lynx", "cheetah", "panther", "bluejay", "raven", "oriole", "barbet", "redfin", "bramble", "sunfish", "coral", "flame"]; +const devices = [ + "comet", + "komodo", + "caiman", + "tokay", + "akita", + "husky", + "shiba", + "felix", + "tangorpro", + "lynx", + "cheetah", + "panther", + "bluejay", + "raven", + "oriole", + "barbet", + "redfin", + "bramble", + "sunfish", + "coral", + "flame", +]; const legacyFactoryDevices = new Set(["sunfish", "coral", "flame"]); const channels = ["stable", "beta", "alpha"]; const delayMs = 1000 * 60 * 5; @@ -11,29 +33,37 @@ async function updateReleases() { for (const channel of channels) { for (const device of devices) { - requests.push(fetch(`${baseUrl}${device}-${channel}`).then(response => { - if (!response.ok) { - return Promise.reject(); - } - return response.text(); - }).then(text => { - const metadata = text.trim().split(" "); - - const factoryFormat = legacyFactoryDevices.has(device) ? "factory" : "install"; - const factoryFilename = `${device}-${factoryFormat}-${metadata[0]}.zip`; - const factoryUrl = baseUrl + factoryFilename; - - const updateFilename = `${device}-ota_update-${metadata[0]}.zip`; - const updateUrl = baseUrl + updateFilename; - - const release = document.getElementById(`${device}-${channel}`); - const links = release.getElementsByTagName("a"); - - links[0].setAttribute("href", "#" + metadata[0]); - links[1].setAttribute("href", factoryUrl); - links[2].setAttribute("href", factoryUrl + ".sig"); - links[3].setAttribute("href", updateUrl); - })); + requests.push( + fetch(`${baseUrl}${device}-${channel}`) + .then((response) => { + if (!response.ok) { + return Promise.reject(); + } + return response.text(); + }) + .then((text) => { + const metadata = text.trim().split(" "); + + const factoryFormat = legacyFactoryDevices.has(device) + ? "factory" + : "install"; + const factoryFilename = `${device}-${factoryFormat}-${metadata[0]}.zip`; + const factoryUrl = baseUrl + factoryFilename; + + const updateFilename = `${device}-ota_update-${metadata[0]}.zip`; + const updateUrl = baseUrl + updateFilename; + + const release = document.getElementById( + `${device}-${channel}`, + ); + const links = release.getElementsByTagName("a"); + + links[0].setAttribute("href", "#" + metadata[0]); + links[1].setAttribute("href", factoryUrl); + links[2].setAttribute("href", factoryUrl + ".sig"); + links[3].setAttribute("href", updateUrl); + }), + ); } } diff --git a/static/js/web-install.js b/static/js/web-install.js index 0f8353b29..8cc6913dc 100644 --- a/static/js/web-install.js +++ b/static/js/web-install.js @@ -12,12 +12,12 @@ const Buttons = { DOWNLOAD_RELEASE: "download-release", FLASH_RELEASE: "flash-release", LOCK_BOOTLOADER: "lock-bootloader", - REMOVE_CUSTOM_KEY: "remove-custom-key" + REMOVE_CUSTOM_KEY: "remove-custom-key", }; const InstallerState = { DOWNLOADING_RELEASE: 0x1, - INSTALLING_RELEASE: 0x2 + INSTALLING_RELEASE: 0x2, }; let wakeLock = null; @@ -107,7 +107,7 @@ class BlobStore { let db = event.target.result; db.createObjectStore("files", { keyPath: "name" }); /* no index needed for such a small database */ - } + }, ); } } @@ -122,7 +122,7 @@ class BlobStore { async loadFile(name) { try { let obj = await this._wrapReq( - this.db.transaction("files").objectStore("files").get(name) + this.db.transaction("files").objectStore("files").get(name), ); return obj.blob; } catch (error) { @@ -145,7 +145,7 @@ class BlobStore { console.log("File saved"); } else { console.log( - `Loaded ${filename} from blob store, skipping download` + `Loaded ${filename} from blob store, skipping download`, ); } @@ -199,7 +199,7 @@ async function unlockBootloader(setProgress) { // Trying to unlock when the bootloader is already unlocked results in a FAIL, // so don't try to do it. - if (await device.getVariable("unlocked") === "yes") { + if ((await device.getVariable("unlocked")) === "yes") { return "Bootloader is already unlocked."; } @@ -208,7 +208,10 @@ async function unlockBootloader(setProgress) { await device.runCommand("flashing unlock"); } catch (error) { // FAIL = user rejected unlock - if (error instanceof fastboot.FastbootError && error.status === "FAIL") { + if ( + error instanceof fastboot.FastbootError && + error.status === "FAIL" + ) { throw new Error("Bootloader was not unlocked, please try again!"); } else { throw error; @@ -218,11 +221,52 @@ async function unlockBootloader(setProgress) { return "Bootloader unlocked."; } -const supportedDevices = ["comet", "komodo", "caiman", "tokay", "akita", "husky", "shiba", "felix", "tangorpro", "lynx", "cheetah", "panther", "bluejay", "raven", "oriole", "barbet", "redfin", "bramble", "sunfish", "coral", "flame"]; +const supportedDevices = [ + "comet", + "komodo", + "caiman", + "tokay", + "akita", + "husky", + "shiba", + "felix", + "tangorpro", + "lynx", + "cheetah", + "panther", + "bluejay", + "raven", + "oriole", + "barbet", + "redfin", + "bramble", + "sunfish", + "coral", + "flame", +]; const legacyQualcommDevices = ["sunfish", "coral", "flame"]; -const day1SnapshotCancelDevices = ["comet", "komodo", "caiman", "tokay", "akita", "husky", "shiba", "felix", "tangorpro", "lynx", "cheetah", "panther", "bluejay", "raven", "oriole", "barbet", "redfin", "bramble"]; +const day1SnapshotCancelDevices = [ + "comet", + "komodo", + "caiman", + "tokay", + "akita", + "husky", + "shiba", + "felix", + "tangorpro", + "lynx", + "cheetah", + "panther", + "bluejay", + "raven", + "oriole", + "barbet", + "redfin", + "bramble", +]; function hasOptimizedFactoryImage(product) { return !legacyQualcommDevices.includes(product); @@ -231,14 +275,19 @@ function hasOptimizedFactoryImage(product) { async function getLatestRelease() { let product = await device.getVariable("product"); if (!supportedDevices.includes(product)) { - throw new Error(`device model (${product}) is not supported by the GrapheneOS web installer`); + throw new Error( + `device model (${product}) is not supported by the GrapheneOS web installer`, + ); } let metadataResp = await fetch(`${RELEASES_URL}/${product}-stable`); let metadata = await metadataResp.text(); let releaseId = metadata.split(" ")[0]; - return [`${product}-${hasOptimizedFactoryImage(product) ? "install" : "factory"}-${releaseId}.zip`, product]; + return [ + `${product}-${hasOptimizedFactoryImage(product) ? "install" : "factory"}-${releaseId}.zip`, + product, + ]; } async function downloadRelease(setProgress) { @@ -246,10 +295,13 @@ async function downloadRelease(setProgress) { await ensureConnected(setProgress); setProgress("Finding latest release..."); - let [latestZip,] = await getLatestRelease(); + let [latestZip] = await getLatestRelease(); // Download and cache the zip as a blob - setInstallerState({ state: InstallerState.DOWNLOADING_RELEASE, active: true }); + setInstallerState({ + state: InstallerState.DOWNLOADING_RELEASE, + active: true, + }); setProgress(`Downloading ${latestZip}...`); await blobStore.init(); try { @@ -257,7 +309,10 @@ async function downloadRelease(setProgress) { setProgress(`Downloading ${latestZip}...`, progress); }); } finally { - setInstallerState({ state: InstallerState.DOWNLOADING_RELEASE, active: false }); + setInstallerState({ + state: InstallerState.DOWNLOADING_RELEASE, + active: false, + }); await releaseWakeLock(); } setProgress(`Downloaded ${latestZip} release.`, 1.0); @@ -306,14 +361,21 @@ async function flashRelease(setProgress) { } setProgress("Flashing release..."); - setInstallerState({ state: InstallerState.INSTALLING_RELEASE, active: true }); + setInstallerState({ + state: InstallerState.INSTALLING_RELEASE, + active: true, + }); try { - await device.flashFactoryZip(blob, true, reconnectCallback, + await device.flashFactoryZip( + blob, + true, + reconnectCallback, (action, item, progress) => { let userAction = fastboot.USER_ACTION_MAP[action]; - let userItem = item === "avb_custom_key" ? "verified boot key" : item; + let userItem = + item === "avb_custom_key" ? "verified boot key" : item; setProgress(`${userAction} ${userItem}...`, progress); - } + }, ); if (legacyQualcommDevices.includes(product)) { setProgress("Disabling UART..."); @@ -329,7 +391,10 @@ async function flashRelease(setProgress) { await device.runCommand("erase:msadp_b"); } } finally { - setInstallerState({ state: InstallerState.INSTALLING_RELEASE, active: false }); + setInstallerState({ + state: InstallerState.INSTALLING_RELEASE, + active: false, + }); await releaseWakeLock(); } @@ -357,7 +422,10 @@ async function lockBootloader(setProgress) { await device.runCommand("flashing lock"); } catch (error) { // FAIL = user rejected lock - if (error instanceof fastboot.FastbootError && error.status === "FAIL") { + if ( + error instanceof fastboot.FastbootError && + error.status === "FAIL" + ) { throw new Error("Bootloader was not locked, please try again!"); } else { throw error; @@ -451,7 +519,10 @@ fastboot.setDebugLevel(2); fastboot.configureZip({ workerScripts: { - inflate: ["/js/fastboot/ffe7e270/vendor/z-worker-pako.js", "pako_inflate.min.js"], + inflate: [ + "/js/fastboot/ffe7e270/vendor/z-worker-pako.js", + "pako_inflate.min.js", + ], }, }); @@ -466,7 +537,7 @@ if ("usb" in navigator) { } // This will create an alert box to stop the user from leaving the page during actions -window.addEventListener("beforeunload", event => { +window.addEventListener("beforeunload", (event) => { if (!safeToLeave()) { console.log("User tried to leave the page whilst unsafe to leave!"); event.returnValue = "";