Skip to content

Commit

Permalink
Add extension support and work on auto update
Browse files Browse the repository at this point in the history
  • Loading branch information
oxmc committed Dec 20, 2024
1 parent 6839090 commit 9c4a886
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 40 deletions.
52 changes: 27 additions & 25 deletions .github/workflows/build-and-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ jobs:
- name: Check the appimage(s)
run: ./appimagelint-x86_64.AppImage dist/*.AppImage

- name: Generate checksum
run: |
sha256sum dist/*.AppImage > sha256sum.txt
- name: Upload Linux Artifacts
uses: actions/upload-artifact@v4
Expand All @@ -54,16 +58,7 @@ jobs:
path: |
dist/*.AppImage
dist/latest*.yml
- name: Generate checksum
run: |
sha256sum dist/*.AppImage > sha256sum.txt
- name: Upload checksums
uses: actions/upload-artifact@v4
with:
name: checksums
path: sha256sum.txt
sha256sum.txt
build-windows:
name: Build bsky-desktop (Windows)
Expand All @@ -89,6 +84,10 @@ jobs:

- name: Build (arm64)
run: npm run build -- --arch arm64

- name: Generate checksum
run: |
sha256sum dist/*.exe > sha256sum.txt
- name: Upload Windows Artifacts
uses: actions/upload-artifact@v4
Expand All @@ -98,6 +97,7 @@ jobs:
path: |
dist/*.exe
dist/latest*.yml
sha256sum.txt
build-macos:
name: Build bsky-desktop (macOS)
Expand All @@ -123,6 +123,11 @@ jobs:

- name: Build (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
uses: actions/upload-artifact@v4
Expand All @@ -132,6 +137,7 @@ jobs:
path: |
dist/*.dmg
dist/latest*.yml
sha256sum.txt
release:
name: Create Release
Expand Down Expand Up @@ -170,11 +176,10 @@ jobs:

- name: Display structure of downloaded files
run: ls -R dist

- name: Merge latest .ymls
uses: mikefarah/yq@v4.44.6
with:
cmd: yq eval-all 'select(fileIndex == 0) * select(fileIndex > 0)' dist/*/*.yml > merged.yml

- name: Combine checksums
run: |
cat dist/linux/sha256sum.txt dist/windows/sha256sum.txt dist/macos/sha256sum.txt > sha256sums.txt
- name: Upload Release
id: create_release
Expand All @@ -187,7 +192,7 @@ jobs:
dist/linux/*.AppImage
dist/windows/*.exe
dist/macos/*.dmg
merged.yml
sha256sums.txt
aur:
name: Publish to AUR
Expand All @@ -200,28 +205,25 @@ jobs:
- name: Checkout git repo
uses: actions/checkout@v3

- name: Install jq
run: sudo apt-get update && sudo apt-get install jq -y

- name: Download checksums
- name: Download linux artifacts
uses: actions/download-artifact@v4
with:
name: checksums
path: .
name: linux-artifacts
path: dist/linux

- name: List downloaded files
run: ls -R
run: ls -R dist

- name: Show content of sha256sum.txt
run: cat sha256sum.txt
run: cat dist/linux/sha256sum.txt

- name: Get app version
id: version
uses: pchynoweth/action-get-npm-version@1.1.1

- name: Extract checksum from sha256sum.txt and change build version
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/^pkgver=.*$/pkgver=${{ steps.version.outputs.version }}/" ./build/PKGBUILD
Expand Down
2 changes: 1 addition & 1 deletion build/PKGBUILD
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ package() {
[Desktop Entry]
Name=Bluesky Desktop
Comment=Bluesky Desktop Client
Exec=/usr/bin/bsky-desktop
Exec=/usr/bin/bsky-desktop %u
Icon=bsky-desktop
Terminal=false
Type=Application
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bsky-desktop",
"version": "1.0.8",
"version": "1.0.9",
"description": "A desktop app of bsky.app",
"main": "src/app/main.js",
"scripts": {
Expand Down
37 changes: 36 additions & 1 deletion src/app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const openAboutWindow = require("./about-window/src/index").default;
const badge = require('./badge');
const contextMenu = require('./context-menu');
const autoUpdater = require('./utils/auto-update');
//const loadCRX = require('./utils/loadCRX');
const loadCRX = require('./utils/loadCRX');
const log4js = require("log4js");
const path = require("path");
const fs = require("fs");
Expand Down Expand Up @@ -36,6 +36,7 @@ global.paths = {
temp: path.join(os.tmpdir(), global.appInfo.name),
};
global.paths.updateDir = path.join(global.paths.data, 'update');
global.paths.extensions = path.join(global.paths.data, 'extensions');

// URLs:
global.urls = {
Expand Down Expand Up @@ -119,6 +120,12 @@ if (!fs.existsSync(global.paths.updateDir)) {
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?
if (process.platform !== "win32" && process.platform !== "darwin") {
logger.log("Disabling Hardware Acceleration and Transparent Visuals");
Expand Down Expand Up @@ -240,6 +247,34 @@ function createWindow() {
// Badge count: (use mainWindow as that shows the badge on the taskbar)
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");
splash.show();
//mainWindow.show();
Expand Down
13 changes: 7 additions & 6 deletions src/app/utils/auto-update.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,14 @@ function checkForUpdates() {
// System is ARM64 (mac-arm64)
macArch = 'arm64';
} else {
// System is Intel (mac-x64)
macArch = 'intel';
// System is x64 (mac-x64)
macArch = 'x64';
}
} else {
// macOS 10 is mostly Intel, but some versions are ARM64
macArch = 'intel';
// macOS 10 is mostly x64, but some versions are ARM64
macArch = 'x64';
}
logger.log('System architecture:', macArch);
// Check for updates, and if there are updates, download and install them
logger.log('Checking for updates (mac)...');

Expand All @@ -122,7 +123,7 @@ function checkForUpdates() {
const command = `sudo installer -pkg ${pkgPath} -target /`;

// 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
});
Expand All @@ -136,7 +137,7 @@ function checkForUpdates() {
} else {
console.error(`Shell process exited with code ${code}.`);
}
});
});*/
} else {
// macOS versions before 10 are not supported
logger.error('macOS versions before 10 are not supported, not updating...');
Expand Down
73 changes: 67 additions & 6 deletions src/app/utils/loadCRX.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,91 @@ const path = require('path');
const { session } = require('electron');
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.
* @param {string} crxPath - Path to the .crx file.
* @returns {Promise<string>} - Resolves with the extension ID after loading.
*/
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
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });

// Extract the .crx file
// Read the CRX file
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);
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
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);
console.log(`Extension loaded with ID: ${id}`);
return id;
Expand Down

0 comments on commit 9c4a886

Please sign in to comment.