163 lines
5.5 KiB
JavaScript
163 lines
5.5 KiB
JavaScript
/**
|
|
* Копирует содержимое папки public в папку gitverse.
|
|
* Файлы `.png` не копируются (на GitVerse достаточно WebP и прочих ассетов).
|
|
*
|
|
* По умолчанию:
|
|
* node scripts/sync-gitverse.mjs
|
|
* (копирует projectRoot/public -> projectRoot/gitverse)
|
|
*
|
|
* Можно передать свои пути:
|
|
* node scripts/sync-gitverse.mjs <sourceDir> <targetDir>
|
|
*/
|
|
|
|
import path from "node:path";
|
|
import os from "node:os";
|
|
import fsp from "node:fs/promises";
|
|
import { execFile, spawn } from "node:child_process";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const projectRoot = path.resolve(__dirname, "..");
|
|
const PRESERVE_IN_TARGET = new Set([".git", ".gitverse"]);
|
|
|
|
function shouldSkipGitverseFile(fileName) {
|
|
return path.extname(fileName).toLowerCase() === ".png";
|
|
}
|
|
const DEFAULT_SSH_KEY_PATH = path.join(os.homedir(), ".ssh", "id_ed25519_work");
|
|
const GITVERSE_SITE_URL = "https://mamadaevv.gitverse.site/me/";
|
|
|
|
/** Системный браузер по умолчанию (Windows / macOS / Linux). */
|
|
function openUrlInDefaultBrowser(url) {
|
|
if (typeof url !== "string" || !/^https?:\/\//i.test(url)) return;
|
|
const opts = { detached: true, stdio: "ignore" };
|
|
let child;
|
|
if (process.platform === "win32") {
|
|
child = spawn("cmd", ["/c", "start", "", url], opts);
|
|
} else if (process.platform === "darwin") {
|
|
child = spawn("open", [url], opts);
|
|
} else {
|
|
child = spawn("xdg-open", [url], opts);
|
|
}
|
|
child?.unref();
|
|
}
|
|
|
|
function resolveDir(input, fallback) {
|
|
const raw = input ?? fallback;
|
|
return path.isAbsolute(raw) ? raw : path.resolve(projectRoot, raw);
|
|
}
|
|
|
|
async function ensureDirExists(dirPath, label) {
|
|
try {
|
|
const st = await fsp.stat(dirPath);
|
|
if (!st.isDirectory()) {
|
|
throw new Error(`${label} не является папкой: ${dirPath}`);
|
|
}
|
|
} catch (error) {
|
|
if (error && error.code === "ENOENT") {
|
|
throw new Error(`${label} не найдена: ${dirPath}`);
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function cleanTargetDir(targetDir) {
|
|
await fsp.mkdir(targetDir, { recursive: true });
|
|
const entries = await fsp.readdir(targetDir, { withFileTypes: true });
|
|
|
|
for (const entry of entries) {
|
|
if (PRESERVE_IN_TARGET.has(entry.name)) continue;
|
|
const fullPath = path.join(targetDir, entry.name);
|
|
await fsp.rm(fullPath, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
async function copyDirFiltered(sourceDir, targetDir) {
|
|
await fsp.mkdir(targetDir, { recursive: true });
|
|
const entries = await fsp.readdir(sourceDir, { withFileTypes: true });
|
|
for (const entry of entries) {
|
|
const srcPath = path.join(sourceDir, entry.name);
|
|
const dstPath = path.join(targetDir, entry.name);
|
|
if (entry.isDirectory()) {
|
|
await copyDirFiltered(srcPath, dstPath);
|
|
continue;
|
|
}
|
|
if (!entry.isFile() || shouldSkipGitverseFile(entry.name)) continue;
|
|
await fsp.copyFile(srcPath, dstPath);
|
|
}
|
|
}
|
|
|
|
function runGit(args, cwd) {
|
|
return new Promise((resolve, reject) => {
|
|
const gitEnv = {
|
|
...process.env,
|
|
GIT_SSH_COMMAND: `ssh -i "${DEFAULT_SSH_KEY_PATH}" -o IdentitiesOnly=yes`,
|
|
};
|
|
execFile("git", args, { cwd, encoding: "utf8", env: gitEnv }, (error, stdout, stderr) => {
|
|
if (error) {
|
|
reject(new Error((stderr || error.message || "").trim()));
|
|
return;
|
|
}
|
|
resolve((stdout || "").trim());
|
|
});
|
|
});
|
|
}
|
|
|
|
function makeCommitMessage() {
|
|
const now = new Date();
|
|
const y = now.getFullYear();
|
|
const m = String(now.getMonth() + 1).padStart(2, "0");
|
|
const d = String(now.getDate()).padStart(2, "0");
|
|
const hh = String(now.getHours()).padStart(2, "0");
|
|
const mm = String(now.getMinutes()).padStart(2, "0");
|
|
return `sync public ${y}-${m}-${d} ${hh}:${mm}`;
|
|
}
|
|
|
|
async function commitAndPush(targetDir) {
|
|
const gitDir = path.join(targetDir, ".git");
|
|
await ensureDirExists(gitDir, "Git репозиторий (.git)");
|
|
try {
|
|
await fsp.access(DEFAULT_SSH_KEY_PATH);
|
|
} catch {
|
|
throw new Error(`SSH-ключ не найден: ${DEFAULT_SSH_KEY_PATH}`);
|
|
}
|
|
|
|
await runGit(["add", "."], targetDir);
|
|
const changed = await runGit(["status", "--porcelain"], targetDir);
|
|
|
|
if (!changed) {
|
|
console.log("Изменений для коммита нет.");
|
|
return;
|
|
}
|
|
|
|
const message = makeCommitMessage();
|
|
await runGit(["commit", "-m", message], targetDir);
|
|
await runGit(["push"], targetDir);
|
|
console.log(`Коммит и пуш выполнены: ${message}`);
|
|
openUrlInDefaultBrowser(GITVERSE_SITE_URL);
|
|
console.log(`Открыт в браузере: ${GITVERSE_SITE_URL}`);
|
|
}
|
|
|
|
async function main() {
|
|
const sourceDir = resolveDir(process.argv[2], "public");
|
|
const targetDir = resolveDir(process.argv[3], "gitverse");
|
|
|
|
await ensureDirExists(sourceDir, "Исходная папка");
|
|
|
|
if (sourceDir === targetDir) {
|
|
throw new Error("Исходная и целевая папки совпадают.");
|
|
}
|
|
|
|
await cleanTargetDir(targetDir);
|
|
await copyDirFiltered(sourceDir, targetDir);
|
|
|
|
console.log(
|
|
`Синхронизировано: ${path.relative(projectRoot, sourceDir)} -> ${path.relative(projectRoot, targetDir)} (сохранены .git и .gitverse)`,
|
|
);
|
|
await commitAndPush(targetDir);
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error("Ошибка копирования:", error.message || error);
|
|
process.exit(1);
|
|
});
|