Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 14 additions & 16 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,36 @@ name: Build Linux

on:
push:
branches:
- release/production
tags:
- 'v*'
workflow_dispatch:

permissions:
contents: write

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: 'recursive'

- name: Install Snapcraft
uses: samuelmeuli/action-snapcraft@v1

- name: Use Node.js 20.x
uses: actions/setup-node@v1
uses: actions/setup-node@v4
with:
node-version: 20.x

- name: install dependencies
run: npm i

- name: build
- name: build deb
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: npm run build:linux -- --publish never
run: npm run build:linux -- --linux deb --publish always

- name: publish amd64
uses: snapcore/action-publish@v1
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_EXPORT }}
- name: upload deb artifact
uses: actions/upload-artifact@v4
with:
snap: "${{ github.workspace }}/dist/Raindrop-amd64.snap"
release: stable
name: Raindrop-linux-deb
path: dist/*.deb
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,37 @@
# Build local copy

## Ubuntu / Linux

### Prerequisites
- Node.js 18 or 20 (`node --version`)
- npm 9+ (`npm --version`)
- git

### Build
```bash
git clone --recurse-submodules https://github.com/0xTriboulet/desktop.git
cd desktop
./build.sh
```

### Install
```bash
sudo dpkg -i dist/Raindrop.io_*.deb
raindrop
```

> **Note:** The `.deb` is the recommended artifact on Ubuntu. The AppImage requires
> `libfuse2` which is not installed by default on Ubuntu 22.04+.

### Force a fresh webapp build
```bash
rm -rf webapp/dist/electron/prod/
npm run build:linux
```

---

## Windows
```
git submodule update --init --recursive
git submodule update --remote --merge
Expand All @@ -7,10 +40,12 @@ npm run build:win
```
Then check `dist` folder

---

# Deployment
Run `npm run deploy:prod`

# Local testing tips
## Windows
Uncomment special `identityName` and `publisher` fields in `build/config.js`
Run `add-appxpackage -path appx.appx -AllowUnsigned`
Run `add-appxpackage -path appx.appx -AllowUnsigned`
50 changes: 50 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
# build.sh – Build Raindrop Desktop for Linux
# Usage: ./build.sh
#
# Produces in ./dist/:
# Raindrop.io_<version>_amd64.deb (recommended for Debian/Ubuntu)
# Raindrop.io-<version>.AppImage (portable; requires libfuse2 to run)
# Raindrop-<arch>.snap (Snap Store package)

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"

echo "==> Checking prerequisites..."
command -v node >/dev/null 2>&1 || { echo "ERROR: node is required but not found."; exit 1; }
command -v npm >/dev/null 2>&1 || { echo "ERROR: npm is required but not found."; exit 1; }

NODE_VER=$(node --version | sed 's/v//')
NODE_MAJOR=$(echo "$NODE_VER" | cut -d. -f1)
if [ "$NODE_MAJOR" -lt 18 ]; then
echo "ERROR: Node.js >= 18 required (found $NODE_VER)"
exit 1
fi
echo " Node $(node --version), npm $(npm --version)"

echo "==> Initialising git submodules..."
git submodule update --init --recursive

echo "==> Installing npm dependencies..."
npm install

echo "==> Building Linux targets (AppImage, deb, snap)..."
npm run build:linux

echo ""
echo "==> Build complete. Artifacts in ./dist/:"
ls -lh dist/*.AppImage dist/*.deb dist/*.snap 2>/dev/null || ls dist/

echo ""
echo "To install on Debian/Ubuntu (recommended):"
DEB=$(ls dist/*.deb 2>/dev/null | head -1)
if [ -n "$DEB" ]; then
echo " sudo dpkg -i $DEB"
echo " Then launch: raindrop"
fi

echo ""
echo "NOTE: The AppImage requires libfuse2 (not present by default on Ubuntu 22.04+)."
echo " Use the .deb package above instead."
8 changes: 6 additions & 2 deletions build/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ exports.default = ()=>({
executableName: 'raindrop',
icon: 'build/linux',
category: 'GNOME;GTK;Network;Education;Science',
target: ['snap'],
// AppImage and deb run outside snap confinement so GPU/display issues don't apply.
// snap kept for Snap Store publishing.
target: ['AppImage', 'deb', 'snap'],
desktop: {
entry: {
StartupWMClass: 'Raindrop.io',
Expand All @@ -101,6 +103,8 @@ exports.default = ()=>({
},

snap: {
artifactName: 'Raindrop-${arch}.${ext}'
artifactName: 'Raindrop-${arch}.${ext}',
// Grant display and GPU access so the snap version works without manual plug connections.
plugs: ['x11', 'wayland', 'desktop', 'desktop-legacy', 'opengl', 'home', 'network', 'browser-support']
}
})
11 changes: 7 additions & 4 deletions build/version.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import fs from 'fs'
import { resolve } from 'path'
import { resolve, dirname } from 'path'
import { fileURLToPath } from 'url'

const version = JSON.parse(fs.readFileSync(resolve(import.meta.dirname, '../webapp/package.json'), 'utf-8')).version;
const packageContent = fs.readFileSync(resolve(import.meta.dirname, '../package.json'), 'utf-8');
fs.writeFileSync(resolve(import.meta.dirname, '../package.json'), packageContent.replace(/version": "(.*)"/i, `version": "${version}"`), 'utf-8');
const __dirname = dirname(fileURLToPath(import.meta.url))

const version = JSON.parse(fs.readFileSync(resolve(__dirname, '../webapp/package.json'), 'utf-8')).version;
const packageContent = fs.readFileSync(resolve(__dirname, '../package.json'), 'utf-8');
fs.writeFileSync(resolve(__dirname, '../package.json'), packageContent.replace(/version": "(.*)"/i, `version": "${version}"`), 'utf-8');
31 changes: 28 additions & 3 deletions build/webapp.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
const util = require('util')
const exec = util.promisify(require('child_process').exec)
const { execSync } = require('child_process')
const fs = require('fs')
const path = require('path')

exports.default = async function(context) {
await exec('cd webapp && npm i && npm run build:electron')
const distIndex = path.resolve(__dirname, '../webapp/dist/electron/prod/index.html')

// Skip webapp build if already built (avoids Sentry CLI auth failure in local/offline builds).
// To force a rebuild, delete webapp/dist/electron/prod/ and re-run.
if (fs.existsSync(distIndex)) {
console.log(' • webapp dist already present, skipping webapp build')
return
}

// The webapp's webpack config includes a Sentry CLI plugin that exits non-zero when
// SENTRY_AUTH_TOKEN is missing. The dist files are still produced, so we tolerate the
// error and verify the output exists afterwards.
const env = { ...process.env }
if (!env.SENTRY_AUTH_TOKEN) env.SENTRY_AUTH_TOKEN = ''

try {
execSync('cd webapp && npm i && npm run build:electron', { env, stdio: 'inherit' })
} catch (e) {
// Check if dist was created despite the error (Sentry CLI failure is non-fatal)
if (fs.existsSync(distIndex)) {
console.log(' • webapp build exited with error (likely Sentry CLI) but dist was produced — continuing')
return
}
throw new Error('webapp build failed and no dist was produced: ' + e.message)
}
}
6 changes: 6 additions & 0 deletions src/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ import webview from './webview/index.mjs'
// Fix webview fail for Twitter
app.commandLine.appendSwitch('disable-features', 'CrossOriginOpenerPolicy')

// Linux display compatibility: auto-select Wayland when available, fall back to X11.
// This must be set before app.whenReady() so it also takes effect when a second
// instance is spawned by the OS to handle rnio:// deep-links (OAuth callback).
if (process.platform === 'linux')
app.commandLine.appendSwitch('ozone-platform-hint', 'auto')

// Security (snap doesn't work properly with sandbox)
if (process.platform !== 'linux')
app.enableSandbox()
Expand Down