Add extension support and work on auto update

This commit is contained in:
oxmc
2024-12-20 08:22:49 -08:00
parent 6839090631
commit 9c4a886799
6 changed files with 139 additions and 40 deletions

View File

@@ -46,6 +46,10 @@ jobs:
- name: Check the appimage(s) - name: Check the appimage(s)
run: ./appimagelint-x86_64.AppImage dist/*.AppImage run: ./appimagelint-x86_64.AppImage dist/*.AppImage
- name: Generate checksum
run: |
sha256sum dist/*.AppImage > sha256sum.txt
- name: Upload Linux Artifacts - name: Upload Linux Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
id: upload-artifact id: upload-artifact
@@ -54,16 +58,7 @@ jobs:
path: | path: |
dist/*.AppImage dist/*.AppImage
dist/latest*.yml dist/latest*.yml
sha256sum.txt
- name: Generate checksum
run: |
sha256sum dist/*.AppImage > sha256sum.txt
- name: Upload checksums
uses: actions/upload-artifact@v4
with:
name: checksums
path: sha256sum.txt
build-windows: build-windows:
name: Build bsky-desktop (Windows) name: Build bsky-desktop (Windows)
@@ -90,6 +85,10 @@ jobs:
- name: Build (arm64) - name: Build (arm64)
run: npm run build -- --arch arm64 run: npm run build -- --arch arm64
- name: Generate checksum
run: |
sha256sum dist/*.exe > sha256sum.txt
- name: Upload Windows Artifacts - name: Upload Windows Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
id: upload-artifact id: upload-artifact
@@ -98,6 +97,7 @@ jobs:
path: | path: |
dist/*.exe dist/*.exe
dist/latest*.yml dist/latest*.yml
sha256sum.txt
build-macos: build-macos:
name: Build bsky-desktop (macOS) name: Build bsky-desktop (macOS)
@@ -124,6 +124,11 @@ jobs:
- name: Build (arm64) - name: Build (arm64)
run: npm run build -- --arch arm64 run: npm run build -- --arch arm64
- name: Generate checksum
run: |
sha256sum dist/*.dmg > sha256sum.txt
# sha256sum dist/*.pkg >> sha256sum.txt
- name: Upload macOS Artifacts - name: Upload macOS Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
id: upload-artifact id: upload-artifact
@@ -132,6 +137,7 @@ jobs:
path: | path: |
dist/*.dmg dist/*.dmg
dist/latest*.yml dist/latest*.yml
sha256sum.txt
release: release:
name: Create Release name: Create Release
@@ -171,10 +177,9 @@ jobs:
- name: Display structure of downloaded files - name: Display structure of downloaded files
run: ls -R dist run: ls -R dist
- name: Merge latest .ymls - name: Combine checksums
uses: mikefarah/yq@v4.44.6 run: |
with: cat dist/linux/sha256sum.txt dist/windows/sha256sum.txt dist/macos/sha256sum.txt > sha256sums.txt
cmd: yq eval-all 'select(fileIndex == 0) * select(fileIndex > 0)' dist/*/*.yml > merged.yml
- name: Upload Release - name: Upload Release
id: create_release id: create_release
@@ -187,7 +192,7 @@ jobs:
dist/linux/*.AppImage dist/linux/*.AppImage
dist/windows/*.exe dist/windows/*.exe
dist/macos/*.dmg dist/macos/*.dmg
merged.yml sha256sums.txt
aur: aur:
name: Publish to AUR name: Publish to AUR
@@ -200,20 +205,17 @@ jobs:
- name: Checkout git repo - name: Checkout git repo
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install jq - name: Download linux artifacts
run: sudo apt-get update && sudo apt-get install jq -y
- name: Download checksums
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: checksums name: linux-artifacts
path: . path: dist/linux
- name: List downloaded files - name: List downloaded files
run: ls -R run: ls -R dist
- name: Show content of sha256sum.txt - name: Show content of sha256sum.txt
run: cat sha256sum.txt run: cat dist/linux/sha256sum.txt
- name: Get app version - name: Get app version
id: version id: version
@@ -221,7 +223,7 @@ jobs:
- name: Extract checksum from sha256sum.txt and change build version - name: Extract checksum from sha256sum.txt and change build version
run: | run: |
new_checksum=$(awk 'NR==1 { print $1 }' ./sha256sum.txt) new_checksum=$(awk 'NR==1 { print $1 }' ./dist/linux/sha256sum.txt)
sed -i "s|sha256sums=('SKIP' 'SKIP')|sha256sums=('$new_checksum' 'SKIP')|" ./build/PKGBUILD sed -i "s|sha256sums=('SKIP' 'SKIP')|sha256sums=('$new_checksum' 'SKIP')|" ./build/PKGBUILD
sed -i "s/^pkgver=.*$/pkgver=${{ steps.version.outputs.version }}/" ./build/PKGBUILD sed -i "s/^pkgver=.*$/pkgver=${{ steps.version.outputs.version }}/" ./build/PKGBUILD

View File

@@ -70,7 +70,7 @@ package() {
[Desktop Entry] [Desktop Entry]
Name=Bluesky Desktop Name=Bluesky Desktop
Comment=Bluesky Desktop Client Comment=Bluesky Desktop Client
Exec=/usr/bin/bsky-desktop Exec=/usr/bin/bsky-desktop %u
Icon=bsky-desktop Icon=bsky-desktop
Terminal=false Terminal=false
Type=Application Type=Application

View File

@@ -1,6 +1,6 @@
{ {
"name": "bsky-desktop", "name": "bsky-desktop",
"version": "1.0.8", "version": "1.0.9",
"description": "A desktop app of bsky.app", "description": "A desktop app of bsky.app",
"main": "src/app/main.js", "main": "src/app/main.js",
"scripts": { "scripts": {

View File

@@ -7,7 +7,7 @@ const openAboutWindow = require("./about-window/src/index").default;
const badge = require('./badge'); const badge = require('./badge');
const contextMenu = require('./context-menu'); const contextMenu = require('./context-menu');
const autoUpdater = require('./utils/auto-update'); const autoUpdater = require('./utils/auto-update');
//const loadCRX = require('./utils/loadCRX'); const loadCRX = require('./utils/loadCRX');
const log4js = require("log4js"); const log4js = require("log4js");
const path = require("path"); const path = require("path");
const fs = require("fs"); const fs = require("fs");
@@ -36,6 +36,7 @@ global.paths = {
temp: path.join(os.tmpdir(), global.appInfo.name), temp: path.join(os.tmpdir(), global.appInfo.name),
}; };
global.paths.updateDir = path.join(global.paths.data, 'update'); global.paths.updateDir = path.join(global.paths.data, 'update');
global.paths.extensions = path.join(global.paths.data, 'extensions');
// URLs: // URLs:
global.urls = { global.urls = {
@@ -119,6 +120,12 @@ if (!fs.existsSync(global.paths.updateDir)) {
fs.mkdirSync(global.paths.updateDir, { recursive: true }); fs.mkdirSync(global.paths.updateDir, { recursive: true });
}; };
// Create extensions directory if it does not exist:
if (!fs.existsSync(global.paths.extensions)) {
logger.info("Creating Extensions Directory");
fs.mkdirSync(global.paths.extensions, { recursive: true });
};
// improve performance on linux? // improve performance on linux?
if (process.platform !== "win32" && process.platform !== "darwin") { if (process.platform !== "win32" && process.platform !== "darwin") {
logger.log("Disabling Hardware Acceleration and Transparent Visuals"); logger.log("Disabling Hardware Acceleration and Transparent Visuals");
@@ -240,6 +247,34 @@ function createWindow() {
// Badge count: (use mainWindow as that shows the badge on the taskbar) // Badge count: (use mainWindow as that shows the badge on the taskbar)
new badge(mainWindow, badgeOptions); new badge(mainWindow, badgeOptions);
// Load extensions (.crx files):
logger.log("Checking for extensions");
const extensions = fs.readdirSync(global.paths.extensions).filter((file) => file.endsWith('.crx'));
if (extensions.length > 0) {
logger.log(`Unpacking ${extensions.length} extensions and loading them`);
extensions.forEach((extension) => {
loadCRX(path.join(global.paths.extensions, extension));
});
} else {
// Check for unpacked extensions:
const unpackedExtensions = fs.readdirSync(global.paths.extensions).filter((file) => fs.lstatSync(path.join(global.paths.extensions, file)).isDirectory());
// Check if the directory contains a manifest.json file
unpackedExtensions.forEach((extension) => {
const manifestPath = path.join(global.paths.extensions, extension, 'manifest.json');
if (fs.existsSync(manifestPath)) {
logger.log(`Loading unpacked extension: ${extension}`);
session.defaultSession.loadExtension(path.join(global.paths.extensions, extension)).then(({ id }) => {
logger.log(`Extension loaded with ID: ${id}`);
}).catch((error) => {
logger.error(`Failed to load extension: ${error}`);
});
} else {
logger.warn(`Skipping directory ${extension} as it does not contain a manifest.json file`);
};
});
};
logger.log("Main Window Created, Showing splashscreen"); logger.log("Main Window Created, Showing splashscreen");
splash.show(); splash.show();
//mainWindow.show(); //mainWindow.show();

View File

@@ -107,13 +107,14 @@ function checkForUpdates() {
// System is ARM64 (mac-arm64) // System is ARM64 (mac-arm64)
macArch = 'arm64'; macArch = 'arm64';
} else { } else {
// System is Intel (mac-x64) // System is x64 (mac-x64)
macArch = 'intel'; macArch = 'x64';
} }
} else { } else {
// macOS 10 is mostly Intel, but some versions are ARM64 // macOS 10 is mostly x64, but some versions are ARM64
macArch = 'intel'; macArch = 'x64';
} }
logger.log('System architecture:', macArch);
// Check for updates, and if there are updates, download and install them // Check for updates, and if there are updates, download and install them
logger.log('Checking for updates (mac)...'); logger.log('Checking for updates (mac)...');
@@ -122,7 +123,7 @@ function checkForUpdates() {
const command = `sudo installer -pkg ${pkgPath} -target /`; const command = `sudo installer -pkg ${pkgPath} -target /`;
// Spawn a new shell // Spawn a new shell
const shellProcess = spawn('sh', ['-c', command], { /*const shellProcess = spawn('sh', ['-c', command], {
stdio: 'inherit', // Pipe input/output to/from the shell stdio: 'inherit', // Pipe input/output to/from the shell
}); });
@@ -136,7 +137,7 @@ function checkForUpdates() {
} else { } else {
console.error(`Shell process exited with code ${code}.`); console.error(`Shell process exited with code ${code}.`);
} }
}); });*/
} else { } else {
// macOS versions before 10 are not supported // macOS versions before 10 are not supported
logger.error('macOS versions before 10 are not supported, not updating...'); logger.error('macOS versions before 10 are not supported, not updating...');

View File

@@ -3,30 +3,91 @@ const path = require('path');
const { session } = require('electron'); const { session } = require('electron');
const AdmZip = require('adm-zip'); const AdmZip = require('adm-zip');
/**
* Converts a CRX file buffer to a ZIP buffer.
* @param {Buffer} buf - The CRX file buffer.
* @returns {Buffer} - The ZIP buffer extracted from the CRX file.
*/
function crxToZip(buf) {
function calcLength(a, b, c, d) {
let length = 0;
length += a << 0;
length += b << 8;
length += c << 16;
length += (d << 24) >>> 0;
return length;
}
// Check if the file is already a ZIP file
if (buf[0] === 80 && buf[1] === 75 && buf[2] === 3 && buf[3] === 4) {
return buf;
}
// Validate CRX magic number
if (buf[0] !== 67 || buf[1] !== 114 || buf[2] !== 50 || buf[3] !== 52) {
throw new Error('Invalid CRX file: Missing Cr24 magic number');
}
const version = buf[4];
const isV2 = version === 2;
const isV3 = version === 3;
if ((!isV2 && !isV3) || buf[5] || buf[6] || buf[7]) {
throw new Error('Unsupported CRX format version.');
}
if (isV2) {
const publicKeyLength = calcLength(buf[8], buf[9], buf[10], buf[11]);
const signatureLength = calcLength(buf[12], buf[13], buf[14], buf[15]);
const zipStartOffset = 16 + publicKeyLength + signatureLength;
return buf.slice(zipStartOffset);
}
const headerSize = calcLength(buf[8], buf[9], buf[10], buf[11]);
const zipStartOffset = 12 + headerSize;
return buf.slice(zipStartOffset);
}
/** /**
* Unpacks a .crx file and loads it as an Electron extension. * Unpacks a .crx file and loads it as an Electron extension.
* @param {string} crxPath - Path to the .crx file. * @param {string} crxPath - Path to the .crx file.
* @returns {Promise<string>} - Resolves with the extension ID after loading. * @returns {Promise<string>} - Resolves with the extension ID after loading.
*/ */
async function loadCRX(crxPath) { async function loadCRX(crxPath) {
const outputDir = path.join(__dirname, 'extensions', path.basename(crxPath, '.crx')); const outputDir = path.join(global.paths.extensions, path.basename(crxPath, '.crx'));
// Ensure the output directory exists // Ensure the output directory exists
if (!fs.existsSync(outputDir)) { if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true }); fs.mkdirSync(outputDir, { recursive: true });
// Extract the .crx file // Read the CRX file
const crxData = fs.readFileSync(crxPath); const crxData = fs.readFileSync(crxPath);
const crxHeaderSize = crxData.readUInt32LE(8); // Extract header size from CRX
const zipData = crxData.slice(crxHeaderSize);
// Save the ZIP content // Convert CRX to ZIP
const zipData = crxToZip(crxData);
// Extract ZIP using AdmZip
const zip = new AdmZip(zipData); const zip = new AdmZip(zipData);
zip.extractAllTo(outputDir, true); zip.getEntries().forEach((entry) => {
const fullPath = path.join(outputDir, entry.entryName);
if (entry.isDirectory) {
fs.mkdirSync(fullPath, { recursive: true });
} else {
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
fs.writeFileSync(fullPath, entry.getData());
}
});
} }
// Load the unpacked extension into Electron // Load the unpacked extension into Electron
try { try {
// Check for manifest.json
const manifestPath = path.join(outputDir, 'manifest.json');
if (!fs.existsSync(manifestPath)) {
throw new Error('Extension is missing manifest.json');
};
// Load the extension
const { id } = await session.defaultSession.loadExtension(outputDir); const { id } = await session.defaultSession.loadExtension(outputDir);
console.log(`Extension loaded with ID: ${id}`); console.log(`Extension loaded with ID: ${id}`);
return id; return id;