mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-06-22 06:03:49 +08:00
add nixos support
This commit is contained in:
@@ -14,6 +14,10 @@ dist-electron
|
|||||||
release
|
release
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
|
# Nix build outputs
|
||||||
|
/result
|
||||||
|
/result-*
|
||||||
|
|
||||||
# Local secrets and databases
|
# Local secrets and databases
|
||||||
.env
|
.env
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ipcMain, WebContents } from "electron";
|
import { ipcMain, WebContents } from "electron";
|
||||||
import { spawn, ChildProcess, exec } from "node:child_process";
|
import { spawn, ChildProcess } from "node:child_process";
|
||||||
import { promisify } from "node:util";
|
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import pino from "pino";
|
import pino from "pino";
|
||||||
@@ -44,28 +43,55 @@ type InstallTask = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SHELL_CALLER_PATH = "/opt/spark-store/extras/shell-caller.sh";
|
const SHELL_CALLER_PATH = "/opt/spark-store/extras/shell-caller.sh";
|
||||||
|
const SUPER_USER_COMMAND_CANDIDATES = [
|
||||||
|
"/usr/bin/pkexec",
|
||||||
|
"/run/wrappers/bin/pkexec",
|
||||||
|
"pkexec",
|
||||||
|
];
|
||||||
|
|
||||||
export const tasks = new Map<number, InstallTask>();
|
export const tasks = new Map<number, InstallTask>();
|
||||||
|
|
||||||
let idle = true; // Indicates if the installation manager is idle
|
let idle = true; // Indicates if the installation manager is idle
|
||||||
|
|
||||||
export const checkSuperUserCommand = async (): Promise<string> => {
|
const findExecutable = async (command: string): Promise<string> => {
|
||||||
let superUserCmd = "";
|
if (path.isAbsolute(command)) {
|
||||||
const execAsync = promisify(exec);
|
try {
|
||||||
if (process.getuid && process.getuid() !== 0) {
|
await fs.promises.access(command, fs.constants.X_OK);
|
||||||
const { stdout, stderr } = await execAsync("which /usr/bin/pkexec");
|
return command;
|
||||||
if (stderr) {
|
} catch {
|
||||||
logger.error("没有找到 pkexec 命令");
|
return "";
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.info(`找到提升权限命令: ${stdout.trim()}`);
|
|
||||||
superUserCmd = stdout.trim();
|
|
||||||
|
|
||||||
if (superUserCmd.length === 0) {
|
|
||||||
logger.error("没有找到提升权限的命令 pkexec!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return superUserCmd;
|
|
||||||
|
return await new Promise((resolve) => {
|
||||||
|
const child = spawn("which", [command]);
|
||||||
|
let stdout = "";
|
||||||
|
|
||||||
|
child.stdout?.on("data", (data) => {
|
||||||
|
stdout += data.toString();
|
||||||
|
});
|
||||||
|
child.on("close", (code) => {
|
||||||
|
resolve(code === 0 ? stdout.trim() : "");
|
||||||
|
});
|
||||||
|
child.on("error", () => {
|
||||||
|
resolve("");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkSuperUserCommand = async (): Promise<string> => {
|
||||||
|
if (process.getuid?.() === 0) return "";
|
||||||
|
|
||||||
|
for (const command of SUPER_USER_COMMAND_CANDIDATES) {
|
||||||
|
const superUserCmd = await findExecutable(command);
|
||||||
|
if (superUserCmd.length > 0) {
|
||||||
|
logger.info(`找到提升权限命令: ${superUserCmd}`);
|
||||||
|
return superUserCmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.error("没有找到提升权限的命令 pkexec!");
|
||||||
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const runCommandCapture = async (execCommand: string, execParams: string[]) => {
|
const runCommandCapture = async (execCommand: string, execParams: string[]) => {
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ import pino from "pino";
|
|||||||
const logger = pino({ name: "shared-installer" });
|
const logger = pino({ name: "shared-installer" });
|
||||||
|
|
||||||
export const SHELL_CALLER_PATH = "/opt/spark-store/extras/shell-caller.sh";
|
export const SHELL_CALLER_PATH = "/opt/spark-store/extras/shell-caller.sh";
|
||||||
|
const SUPER_USER_COMMAND_CANDIDATES = [
|
||||||
|
"/usr/bin/pkexec",
|
||||||
|
"/run/wrappers/bin/pkexec",
|
||||||
|
"pkexec",
|
||||||
|
];
|
||||||
|
|
||||||
export interface DownloadOptions {
|
export interface DownloadOptions {
|
||||||
pkgname: string;
|
pkgname: string;
|
||||||
@@ -345,18 +350,39 @@ export const checkApmAvailable = async (): Promise<boolean> => {
|
|||||||
* 检查提权命令
|
* 检查提权命令
|
||||||
*/
|
*/
|
||||||
export const checkSuperUserCommand = async (): Promise<string> => {
|
export const checkSuperUserCommand = async (): Promise<string> => {
|
||||||
return new Promise((resolve) => {
|
if (process.getuid?.() === 0) return "";
|
||||||
const child = spawn("which", ["/usr/bin/pkexec"]);
|
|
||||||
|
for (const command of SUPER_USER_COMMAND_CANDIDATES) {
|
||||||
|
const superUserCmd = await findExecutable(command);
|
||||||
|
if (superUserCmd.length > 0) {
|
||||||
|
logger.info(`找到提升权限命令: ${superUserCmd}`);
|
||||||
|
return superUserCmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.error("没有找到提升权限的命令 pkexec!");
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const findExecutable = async (command: string): Promise<string> => {
|
||||||
|
if (path.isAbsolute(command)) {
|
||||||
|
try {
|
||||||
|
await fs.promises.access(command, fs.constants.X_OK);
|
||||||
|
return command;
|
||||||
|
} catch {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await new Promise((resolve) => {
|
||||||
|
const child = spawn("which", [command]);
|
||||||
let stdout = "";
|
let stdout = "";
|
||||||
|
|
||||||
child.stdout?.on("data", (data) => {
|
child.stdout?.on("data", (data) => {
|
||||||
stdout += data.toString();
|
stdout += data.toString();
|
||||||
});
|
});
|
||||||
child.on("close", (code) => {
|
child.on("close", (code) => {
|
||||||
if (code === 0) {
|
resolve(code === 0 ? stdout.trim() : "");
|
||||||
resolve(stdout.trim());
|
|
||||||
} else {
|
|
||||||
resolve("");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
child.on("error", () => {
|
child.on("error", () => {
|
||||||
resolve("");
|
resolve("");
|
||||||
|
|||||||
+158
@@ -0,0 +1,158 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
buildNpmPackage,
|
||||||
|
importNpmLock,
|
||||||
|
electron,
|
||||||
|
makeWrapper,
|
||||||
|
aria2,
|
||||||
|
apm,
|
||||||
|
coreutils,
|
||||||
|
gnugrep,
|
||||||
|
which,
|
||||||
|
xdg-utils,
|
||||||
|
bash,
|
||||||
|
}:
|
||||||
|
|
||||||
|
buildNpmPackage rec {
|
||||||
|
pname = "spark-store";
|
||||||
|
version = "5.1.1";
|
||||||
|
|
||||||
|
src = lib.cleanSourceWith {
|
||||||
|
src = ../.;
|
||||||
|
filter =
|
||||||
|
path: type:
|
||||||
|
let
|
||||||
|
baseName = baseNameOf path;
|
||||||
|
in
|
||||||
|
!(lib.elem baseName [
|
||||||
|
".git"
|
||||||
|
"dist"
|
||||||
|
"dist-electron"
|
||||||
|
"node_modules"
|
||||||
|
"release"
|
||||||
|
"result"
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
npmDeps = importNpmLock {
|
||||||
|
npmRoot = ../.;
|
||||||
|
};
|
||||||
|
npmConfigHook = importNpmLock.npmConfigHook;
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
makeWrapper
|
||||||
|
];
|
||||||
|
|
||||||
|
env = {
|
||||||
|
ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
|
||||||
|
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "1";
|
||||||
|
};
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
runHook preBuild
|
||||||
|
|
||||||
|
npm run build:vite
|
||||||
|
|
||||||
|
runHook postBuild
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
appDir="$out/share/spark-store"
|
||||||
|
|
||||||
|
npm prune --omit=dev --ignore-scripts
|
||||||
|
|
||||||
|
substituteInPlace extras/shell-caller.sh \
|
||||||
|
--replace-fail "/usr/bin/apm" "${lib.getExe' apm "apm"}"
|
||||||
|
|
||||||
|
mkdir -p "$appDir" "$out/bin"
|
||||||
|
cp -r dist dist-electron package.json node_modules extras icons "$appDir"/
|
||||||
|
|
||||||
|
chmod -R u+w "$appDir"
|
||||||
|
find "$appDir/extras" -type f -exec chmod +x {} \;
|
||||||
|
|
||||||
|
substituteInPlace "$appDir/dist-electron/main/index.js" \
|
||||||
|
--replace-fail "/opt/spark-store/extras/shell-caller.sh" "$appDir/extras/shell-caller.sh" \
|
||||||
|
--replace-fail "/opt/spark-store/extras/app-launcher" "$appDir/extras/app-launcher"
|
||||||
|
|
||||||
|
install -Dm644 pkg/usr/share/applications/spark-store.desktop \
|
||||||
|
"$out/share/applications/spark-store.desktop"
|
||||||
|
|
||||||
|
install -Dm644 icons/spark-store.svg \
|
||||||
|
"$out/share/icons/hicolor/scalable/apps/spark-store.svg"
|
||||||
|
|
||||||
|
install -Dm644 icons/spark-store.png \
|
||||||
|
"$out/share/icons/hicolor/512x512/apps/spark-store.png"
|
||||||
|
|
||||||
|
install -Dm644 extras/store.spark-app.spark-store.policy \
|
||||||
|
"$out/share/polkit-1/actions/store.spark-app.spark-store.policy"
|
||||||
|
|
||||||
|
substituteInPlace "$out/share/polkit-1/actions/store.spark-app.spark-store.policy" \
|
||||||
|
--replace-fail "/opt/spark-store/extras/shell-caller.sh" "$appDir/extras/shell-caller.sh"
|
||||||
|
|
||||||
|
cat > "$out/bin/spark-store" <<EOF
|
||||||
|
#!${bash}/bin/bash
|
||||||
|
export PATH="${lib.makeBinPath [
|
||||||
|
aria2
|
||||||
|
coreutils
|
||||||
|
gnugrep
|
||||||
|
which
|
||||||
|
xdg-utils
|
||||||
|
]}:\$PATH"
|
||||||
|
|
||||||
|
electron_args=(--no-sandbox)
|
||||||
|
app_args=()
|
||||||
|
|
||||||
|
root_path="\$(${coreutils}/bin/readlink -f /proc/self/root)"
|
||||||
|
if [ "\$root_path" != "/" ]; then
|
||||||
|
app_args+=(--no-apm)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "\''${IS_ACE_ENV:-}" = "1" ]; then
|
||||||
|
app_args+=(--no-apm)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v apt >/dev/null 2>&1; then
|
||||||
|
app_args+=(--no-spark)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -r /etc/os-release ] && ${gnugrep}/bin/grep -q "ID=aosc" /etc/os-release; then
|
||||||
|
app_args+=(--no-spark)
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec ${electron}/bin/electron "\''${electron_args[@]}" "$appDir" "\''${app_args[@]}" "\$@"
|
||||||
|
EOF
|
||||||
|
chmod +x "$out/bin/spark-store"
|
||||||
|
|
||||||
|
patchShebangs "$appDir/extras"
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
postFixup = ''
|
||||||
|
appDir="$out/share/spark-store"
|
||||||
|
|
||||||
|
wrapProgram "$appDir/extras/shell-caller.sh" \
|
||||||
|
--prefix PATH : ${
|
||||||
|
lib.makeBinPath [
|
||||||
|
aria2
|
||||||
|
bash
|
||||||
|
coreutils
|
||||||
|
which
|
||||||
|
xdg-utils
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
substituteInPlace "$appDir/extras/.shell-caller.sh-wrapped" \
|
||||||
|
--replace-fail "#!/bin/bash" "#!${bash}/bin/bash"
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Client for Spark App Store";
|
||||||
|
homepage = "https://spark-app.store";
|
||||||
|
license = lib.licenses.gpl3Only;
|
||||||
|
mainProgram = "spark-store";
|
||||||
|
platforms = lib.platforms.linux;
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user