mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-06-22 06:03:49 +08:00
!399 add nixos support and update readme
Merge pull request !399 from SunnyPai/Erotica
This commit is contained in:
@@ -14,6 +14,10 @@ dist-electron
|
||||
release
|
||||
*.local
|
||||
|
||||
# Nix build outputs
|
||||
/result
|
||||
/result-*
|
||||
|
||||
# Local secrets and databases
|
||||
.env
|
||||
.env.*.local
|
||||
|
||||
@@ -18,7 +18,7 @@ Linux 应用的数量相对有限,Wine 软件的可获取性也颇为困难。
|
||||
|
||||
**当前支持的 Linux 发行版包括(但不限于):**
|
||||
|
||||
- **amd64 架构:** Debian 10+ / Ubuntu 22.04+ / Arch Linux / Fedora / deepin / UOS / 银河麒麟
|
||||
- **amd64 架构:** Debian 10+ / Ubuntu 22.04+ / Arch Linux / Fedora / deepin / UOS / 银河麒麟 / NixOS
|
||||
- **arm64 架构:** Debian 10+ / Ubuntu 22.04+ / Arch Linux / deepin / UOS / 银河麒麟
|
||||
- **loong64 架构:** deepin 23/25
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { ipcMain, WebContents } from "electron";
|
||||
import { spawn, ChildProcess, exec } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
import { spawn, ChildProcess } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import pino from "pino";
|
||||
|
||||
import { ChannelPayload } from "../../typedefinition";
|
||||
import axios from "axios";
|
||||
import { findExecutable, SUPER_USER_COMMAND_CANDIDATES } from "./superuser";
|
||||
|
||||
const logger = pino({ name: "install-manager" });
|
||||
|
||||
@@ -50,22 +50,18 @@ export const tasks = new Map<number, InstallTask>();
|
||||
let idle = true; // Indicates if the installation manager is idle
|
||||
|
||||
export const checkSuperUserCommand = async (): Promise<string> => {
|
||||
let superUserCmd = "";
|
||||
const execAsync = promisify(exec);
|
||||
if (process.getuid && process.getuid() !== 0) {
|
||||
const { stdout, stderr } = await execAsync("which /usr/bin/pkexec");
|
||||
if (stderr) {
|
||||
logger.error("没有找到 pkexec 命令");
|
||||
return;
|
||||
}
|
||||
logger.info(`找到提升权限命令: ${stdout.trim()}`);
|
||||
superUserCmd = stdout.trim();
|
||||
if (process.getuid?.() === 0) return "";
|
||||
|
||||
if (superUserCmd.length === 0) {
|
||||
logger.error("没有找到提升权限的命令 pkexec!");
|
||||
for (const command of SUPER_USER_COMMAND_CANDIDATES) {
|
||||
const superUserCmd = await findExecutable(command);
|
||||
if (superUserCmd.length > 0) {
|
||||
logger.info(`找到提升权限命令: ${superUserCmd}`);
|
||||
return superUserCmd;
|
||||
}
|
||||
}
|
||||
return superUserCmd;
|
||||
|
||||
logger.error("没有找到提升权限的命令 pkexec!");
|
||||
return "";
|
||||
};
|
||||
|
||||
const runCommandCapture = async (execCommand: string, execParams: string[]) => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import axios from "axios";
|
||||
import pino from "pino";
|
||||
import { findExecutable, SUPER_USER_COMMAND_CANDIDATES } from "./superuser";
|
||||
|
||||
const logger = pino({ name: "shared-installer" });
|
||||
|
||||
@@ -345,21 +346,16 @@ export const checkApmAvailable = async (): Promise<boolean> => {
|
||||
* 检查提权命令
|
||||
*/
|
||||
export const checkSuperUserCommand = async (): Promise<string> => {
|
||||
return new Promise((resolve) => {
|
||||
const child = spawn("which", ["/usr/bin/pkexec"]);
|
||||
let stdout = "";
|
||||
child.stdout?.on("data", (data) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
child.on("close", (code) => {
|
||||
if (code === 0) {
|
||||
resolve(stdout.trim());
|
||||
} else {
|
||||
resolve("");
|
||||
}
|
||||
});
|
||||
child.on("error", () => {
|
||||
resolve("");
|
||||
});
|
||||
});
|
||||
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 "";
|
||||
};
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
export const SUPER_USER_COMMAND_CANDIDATES = [
|
||||
"/usr/bin/pkexec",
|
||||
"/run/wrappers/bin/pkexec",
|
||||
];
|
||||
|
||||
const WHICH_TIMEOUT_MS = 5000;
|
||||
|
||||
export 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<string>((resolve) => {
|
||||
const child = spawn("which", [command]);
|
||||
let stdout = "";
|
||||
let settled = false;
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
child.kill();
|
||||
finish("");
|
||||
}, WHICH_TIMEOUT_MS);
|
||||
|
||||
function finish(result: string) {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
clearTimeout(timer);
|
||||
resolve(result);
|
||||
}
|
||||
|
||||
child.stdout?.on("data", (data) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
child.on("close", (code) => {
|
||||
finish(code === 0 ? stdout.trim() : "");
|
||||
});
|
||||
child.on("error", () => {
|
||||
finish("");
|
||||
});
|
||||
});
|
||||
};
|
||||
+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