19 Commits
Author SHA1 Message Date
selimaj-dev dd9dca9072 Merge pull request #16 from selimaj-dev/fixed-arch
Fixed arch
2026-03-22 12:32:10 +01:00
selimaj-dev e5dcb2815c Merge pull request #17 from selimaj-dev/test
Revert "Merge pull request #15 from postEntropy/fix-multiarch-build-a…
2026-03-22 12:31:53 +01:00
selimaj-dev 13b4ecf357 Revert "Merge pull request #15 from postEntropy/fix-multiarch-build-and-url"
This reverts commit bb11f190b9, reversing
changes made to 77599e880f.
2026-03-22 12:30:57 +01:00
selimaj-dev bb11f190b9 Merge pull request #15 from postEntropy/fix-multiarch-build-and-url
fix: Enable x64 and arm64 cross-platform builds and fix installer CPU arch detection
2026-03-22 12:23:46 +01:00
selimaj-dev a9511ef3d8 Version 0.2.5 2026-03-22 12:22:28 +01:00
selimaj-dev 388d545162 Working for both arm and x64 #15 2026-03-22 12:20:41 +01:00
postEntropy 2947a49252 fix: multi-architecture build and installer URL logic 2026-03-21 09:57:11 -03:00
selimaj-dev 77599e880f Merge pull request #14 from Akuukis/install
fix(installer): update script for ubuntu
2026-03-19 16:22:56 +01:00
Akuukis 1f0d7bc5a1 fix(installer): update script for ubuntu 2026-03-19 09:39:50 +02:00
selimaj-dev 6aba7c178a Merge pull request #13 from selimaj-dev/fixed-build
Fixed build
2026-03-18 14:39:27 +01:00
selimaj-dev 43b7957ae3 idk 2026-03-18 14:38:33 +01:00
selimaj-dev 8ba81269c2 Fixed build errors 2026-03-18 14:36:14 +01:00
selimaj-dev 76d14296a5 Fixed build errors 2026-03-18 14:25:58 +01:00
selimaj-dev ac6c63f2f0 rollback to 0.2.2 #11 2026-03-18 14:14:23 +01:00
Klesti Selimaj c2dfca9082 Update version in README for installer overrides 2025-11-28 19:36:47 +01:00
Klesti Selimaj 6c51e9226e Update version to 0.2.3 in installer script 2025-11-28 19:36:06 +01:00
Klesti Selimaj be22960c6f Merge pull request #10 from zudsniper/feat/window-management-and-fixes
feat: Window shortcuts, state persistence, and build fixes
2025-11-28 19:33:50 +01:00
zudsniper ce28c3ddb7 feat: add window management, shortcuts, and build fixes
- Add Ctrl+Shift+N shortcut to create new windows
- Implement window state persistence (save/restore position and size)
- New windows duplicate the size of the currently focused window with an offset
- Fix Debian package chrome-sandbox permissions via after-install script
- Update build configuration: remove rpm, add author email
2025-11-27 23:26:20 -06:00
selimaj-dev ef412e3d12 installer update 2025-03-20 14:25:15 -05:00
8 changed files with 398 additions and 835 deletions
+65
View File
@@ -0,0 +1,65 @@
name: Release
on:
workflow_dispatch:
inputs:
bump:
description: "Semver bump (patch, minor, major)"
required: false
default: "patch"
push:
branches:
- release
jobs:
build-and-release:
if: github.actor != 'github-actions[bot]'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: true
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: Bump version and tag
id: bump
env:
BUMP: ${{ inputs.bump }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
npm version "${BUMP:-patch}" -m "chore: release %s"
VERSION=$(node -p "require('./package.json').version")
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
git push
git push --tags
- name: Build artifacts
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npm run build
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ steps.bump.outputs.version }}
name: Linear v${{ steps.bump.outputs.version }}
draft: false
prerelease: false
files: |
dist/*.AppImage
dist/*.deb
dist/*.rpm
+15 -2
View File
@@ -11,9 +11,22 @@ A linux port of <a href="https://linear.app/">linear.app</a>
</p>
# Install
Requires `sudo` (for sandbox helper, desktop entry, and icons).
```bash
wget -qO- https://raw.githubusercontent.com/kleo-dev/linear-linux/refs/heads/master/installer.sh | bash
git clone [email protected]:selimaj-dev/linear-linux.git
cd linear-linux
./installer.sh
```
The installer:
- Downloads the AppImage, extracts it under `/opt/linear-linux-<version>`, and wires up the `chrome-sandbox` helper correctly.
- Installs a wrapper at `/usr/local/bin/linear`, a desktop entry, and the Linear icon into the system icon cache.
- Accepts overrides: `VERSION=0.2.5 APPIMAGE_URL=<url> INSTALL_ROOT=/opt ./installer.sh`
Development:
- `npm start` launches Electron with sandbox disabled for local runs (packaged builds use the proper setuid helper).
- `npm run build` produces an AppImage that bundles the Linear brand assets for the desktop icon.
# Having an issue?
Describe your issue [here](https://github.com/kleo-dev/linear-linux/issues)
Describe your issue [here](https://github.com/selimaj-dev/linear-linux/issues)
Binary file not shown.

After

Width:  |  Height:  |  Size: 639 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.4 KiB

+94 -9
View File
@@ -1,24 +1,109 @@
const { app, BrowserWindow } = require('electron');
const { app, BrowserWindow, session, globalShortcut } = require('electron');
const fs = require('fs');
const path = require('path');
let mainWindow;
let stateFilePath;
app.whenReady().then(() => {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
const loadWindowState = () => {
if (!stateFilePath) return {};
try {
const raw = fs.readFileSync(stateFilePath, 'utf8');
const parsed = JSON.parse(raw);
const { width, height, x, y } = parsed;
if (Number.isFinite(width) && Number.isFinite(height)) {
return {
width,
height,
x: Number.isFinite(x) ? x : undefined,
y: Number.isFinite(y) ? y : undefined,
};
}
} catch (_) {
// Ignore malformed or missing state and fall back to defaults.
}
return {};
};
const saveWindowState = (bounds) => {
if (!stateFilePath || !bounds) return;
try {
fs.mkdirSync(path.dirname(stateFilePath), { recursive: true });
fs.writeFileSync(stateFilePath, JSON.stringify(bounds), 'utf8');
} catch (_) {
// Persisting state is best-effort; ignore write failures.
}
};
const createWindow = () => {
let state = {};
const focusedWindow = BrowserWindow.getFocusedWindow();
if (focusedWindow) {
const bounds = focusedWindow.getBounds();
state = {
width: bounds.width,
height: bounds.height,
x: bounds.x + 30,
y: bounds.y + 30
};
} else {
state = loadWindowState();
}
const windowOptions = {
width: state.width || 800,
height: state.height || 600,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
},
};
if (Number.isFinite(state.x) && Number.isFinite(state.y)) {
windowOptions.x = state.x;
windowOptions.y = state.y;
}
const win = new BrowserWindow(windowOptions);
win.setMenu(null);
win.loadURL('https://linear.app/login');
win.on('close', () => {
saveWindowState(win.getBounds());
});
};
app.whenReady().then(() => {
stateFilePath = path.join(app.getPath('userData'), 'window-state.json');
session.defaultSession.setPermissionRequestHandler((_, permission, callback, details) => {
const origin = details?.requestingUrl || '';
const isLinear = origin.startsWith('https://linear.app');
if (permission === 'notifications' && isLinear) {
return callback(true);
}
return callback(false);
});
mainWindow.setMenu(null);
createWindow();
mainWindow.loadURL('https://linear.app/login');
app.on('browser-window-focus', () => {
globalShortcut.register('CommandOrControl+Shift+N', createWindow);
});
app.on('browser-window-blur', () => {
globalShortcut.unregister('CommandOrControl+Shift+N');
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
});
Regular → Executable
+80 -4
View File
@@ -1,4 +1,80 @@
#/bin/bash
wget -O ~/.linear.AppImage https://github.com/kleo-dev/linear-linux/releases/download/v0.2.0/linear-linux-0.2.0.AppImage
sudo chmod +x ~/.linear.AppImage
sudo mv ~/.linear.AppImage /bin/linear
#!/usr/bin/env bash
set -euo pipefail
ARCH=$(uname -m)
# Normalize to electron-builder naming
case "$ARCH" in
x86_64) ARCH="x64" ;;
aarch64 | arm64) ARCH="arm64" ;;
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
esac
VERSION="${VERSION:-0.2.5}"
APPIMAGE_URL="${APPIMAGE_URL:-https://github.com/selimaj-dev/linear-linux/releases/download/v${VERSION}/linear-linux-${VERSION}-${ARCH}.AppImage}"
INSTALL_ROOT="${INSTALL_ROOT:-/opt}"
INSTALL_DIR="${INSTALL_ROOT}/linear-linux-${VERSION}"
WRAPPER_PATH="/usr/local/bin/linear"
DESKTOP_PATH="/usr/share/applications/linear.desktop"
APP_NAME="Linear"
APP_DIR="${INSTALL_DIR}/squashfs-root"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ICON_SVG="${SCRIPT_DIR}/assets/linear-icon.svg"
ICON_PNG="${SCRIPT_DIR}/assets/linear-app-icon.png"
if [[ ! -f "${ICON_SVG}" || ! -f "${ICON_PNG}" ]]; then
echo "Icon assets missing. Expected ${ICON_SVG} and ${ICON_PNG}." >&2
exit 1
fi
tmpdir="$(mktemp -d)"
trap 'rm -rf "${tmpdir}"' EXIT
echo "Downloading Linear AppImage ${VERSION}..."
curl -fsSL "${APPIMAGE_URL}" -o "${tmpdir}/linear.AppImage"
chmod +x "${tmpdir}/linear.AppImage"
echo "Installing to ${INSTALL_DIR}..."
sudo mkdir -p "${INSTALL_DIR}"
sudo cp "${tmpdir}/linear.AppImage" "${INSTALL_DIR}/linear.AppImage"
sudo chmod +x "${INSTALL_DIR}/linear.AppImage"
sudo bash -lc "cd \"${INSTALL_DIR}\" && ./linear.AppImage --appimage-extract >/dev/null"
sudo chmod -R a+rX "${APP_DIR}"
echo "Configuring sandbox helper..."
sudo chown root:root "${APP_DIR}/chrome-sandbox"
sudo chmod 4755 "${APP_DIR}/chrome-sandbox"
echo "Creating launch wrapper at ${WRAPPER_PATH}..."
sudo tee "${WRAPPER_PATH}" >/dev/null <<EOF
#!/usr/bin/env bash
export CHROME_DEVEL_SANDBOX="${APP_DIR}/chrome-sandbox"
exec "${APP_DIR}/AppRun" "\$@"
EOF
sudo chmod 755 "${WRAPPER_PATH}"
echo "Installing desktop entry..."
sudo tee "${DESKTOP_PATH}" >/dev/null <<EOF
[Desktop Entry]
Type=Application
Name=${APP_NAME}
Exec=${WRAPPER_PATH} %U
Icon=linear
Terminal=false
Categories=Utility;
StartupWMClass=linear-linux
EOF
echo "Installing icons..."
sudo install -Dm644 "${ICON_SVG}" /usr/share/icons/hicolor/scalable/apps/linear.svg
sudo install -Dm644 "${ICON_PNG}" /usr/share/icons/hicolor/512x512/apps/linear.png
sudo gtk-update-icon-cache -f /usr/share/icons/hicolor || true
echo "Configuring chrome sandbox"
sudo chown root:root /opt/Linear/chrome-sandbox || true
sudo chmod 4755 /opt/Linear/chrome-sandbox || true
echo "Linear ${VERSION} installed. Launch with: ${WRAPPER_PATH}"
+113 -813
View File
File diff suppressed because it is too large Load Diff
+28 -5
View File
@@ -1,25 +1,48 @@
{
"name": "linear-linux",
"version": "0.2.1",
"version": "0.2.5",
"description": "Linux support for linear.app",
"main": "index.js",
"scripts": {
"start": "npx electron index.js",
"build": "electron-builder --linux AppImage"
"start": "CHROME_DEVEL_SANDBOX=${CHROME_DEVEL_SANDBOX:-/opt/linear-linux/squashfs-root/chrome-sandbox} ELECTRON_DISABLE_SANDBOX=1 npx electron index.js",
"build": "npx electron-builder --linux AppImage --x64 --arm64"
},
"repository": {
"type": "git",
"url": "git+https://github.com/kleo-dev/linear-linux.git"
},
"keywords": [],
"author": "",
"author": "Selimaj Dev",
"license": "ISC",
"bugs": {
"url": "https://github.com/kleo-dev/linear-linux/issues"
},
"homepage": "https://github.com/kleo-dev/linear-linux#readme",
"build": {
"appId": "app.linear.linux",
"productName": "Linear",
"files": [
"index.js",
"package.json",
"assets/**/*"
],
"linux": {
"target": [
"AppImage"
],
"category": "Utility",
"icon": "assets/linear-app-icon.png",
"artifactName": "linear-linux-${version}-${arch}.${ext}"
},
"extraResources": [
{
"from": "assets",
"to": "assets"
}
]
},
"devDependencies": {
"electron": "^33.2.1",
"electron": "33.2.1",
"electron-builder": "^25.1.8"
}
}