1 Commits

Author SHA1 Message Date
sorlinv
68815645a4 fix: auto-update corrige — tag, robustesse UpdateManager, .env securise
- Supprime .env du tracking git (token expose) + ajout .gitignore
- UpdateManager : timeout download 60s, validation root par version.json,
  erreur si fichiers requis manquants, drain response sur redirect,
  gestion redirects 3xx, protection double-clic, logging erreurs
- UpdateBanner : log erreur dans le .catch au lieu de silencieux
- release.sh : tag cree localement avant push pour eviter desync

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 10:28:55 +01:00
5 changed files with 61 additions and 19 deletions

1
.env
View File

@@ -1 +0,0 @@
GITEA_TOKEN=a2e914e25f14b1de8ee5eddea2d18f753db7bbc1

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ dist/
config/saves/ config/saves/
blender/ blender/
*.log *.log
.env

View File

@@ -75,11 +75,13 @@ EOF
fi fi
############################################ ############################################
# Commit + push # Commit + tag local + push
############################################ ############################################
git add package.json version.json git add package.json version.json
git commit -m "chore: release ${TAG}" || true git commit -m "chore: release ${TAG}" || true
git tag -a "${TAG}" -m "Release ${TAG}"
git push git push
git push origin "${TAG}"
############################################ ############################################
# Build # Build

View File

@@ -8,11 +8,15 @@ const { execFile } = require("child_process");
const GITEA_HOST = "git.sorlinv.fr"; const GITEA_HOST = "git.sorlinv.fr";
const REPO_PATH = "/api/v1/repos/sorlinv/multi_render_blender/tags"; const REPO_PATH = "/api/v1/repos/sorlinv/multi_render_blender/tags";
const ARCHIVE_URL_BASE = "https://git.sorlinv.fr/sorlinv/multi_render_blender/archive/"; const ARCHIVE_URL_BASE = "https://git.sorlinv.fr/sorlinv/multi_render_blender/archive/";
const NB_TIMEOUT = 10000; const NB_TIMEOUT_API = 10000;
const NB_TIMEOUT_DOWNLOAD = 60000;
const LIST_REQUIRED_FILES = ["main.js", "version.json"];
const LIST_TARGETS = ["main.js", "preload.js", "src", "version.json", "package.json"];
const UpdateManager = { const UpdateManager = {
obj_window: null, obj_window: null,
str_local_version: null, str_local_version: null,
is_updating: false,
init: (obj_window) => { init: (obj_window) => {
UpdateManager.obj_window = obj_window; UpdateManager.obj_window = obj_window;
@@ -61,12 +65,18 @@ const UpdateManager = {
return null; return null;
}) })
.catch(() => { .catch((obj_err) => {
console.error("UpdateManager: check_for_updates echoue :", obj_err.message);
return null; return null;
}); });
}, },
download_and_apply: (str_tag_name) => { download_and_apply: (str_tag_name) => {
if (UpdateManager.is_updating) {
return Promise.reject(new Error("Mise a jour deja en cours"));
}
UpdateManager.is_updating = true;
let str_temp_dir = path.join(app.getPath("temp"), "mrb_update_" + Date.now()); let str_temp_dir = path.join(app.getPath("temp"), "mrb_update_" + Date.now());
let str_zip_path = path.join(str_temp_dir, "update.zip"); let str_zip_path = path.join(str_temp_dir, "update.zip");
@@ -102,11 +112,14 @@ const UpdateManager = {
nb_percent: 100, nb_percent: 100,
}); });
UpdateManager._cleanup(str_temp_dir); UpdateManager._cleanup(str_temp_dir);
UpdateManager.is_updating = false;
app.relaunch(); app.relaunch();
app.quit(); app.quit();
}) })
.catch((obj_err) => { .catch((obj_err) => {
UpdateManager.is_updating = false;
UpdateManager._cleanup(str_temp_dir); UpdateManager._cleanup(str_temp_dir);
console.error("UpdateManager: download_and_apply echoue :", obj_err.message);
UpdateManager._send_event("update-error", { UpdateManager._send_event("update-error", {
str_message: obj_err.message || "Erreur inconnue", str_message: obj_err.message || "Erreur inconnue",
str_tag_name: str_tag_name, str_tag_name: str_tag_name,
@@ -154,7 +167,7 @@ const UpdateManager = {
hostname: GITEA_HOST, hostname: GITEA_HOST,
path: REPO_PATH, path: REPO_PATH,
method: "GET", method: "GET",
timeout: NB_TIMEOUT, timeout: NB_TIMEOUT_API,
headers: { headers: {
"Accept": "application/json", "Accept": "application/json",
"User-Agent": "MultiRenderBlender", "User-Agent": "MultiRenderBlender",
@@ -208,10 +221,11 @@ const UpdateManager = {
let obj_module = obj_parsed.protocol === "https:" ? https : http; let obj_module = obj_parsed.protocol === "https:" ? https : http;
let obj_req = obj_module.get(str_download_url, { let obj_req = obj_module.get(str_download_url, {
timeout: NB_TIMEOUT, timeout: NB_TIMEOUT_DOWNLOAD,
headers: { "User-Agent": "MultiRenderBlender" }, headers: { "User-Agent": "MultiRenderBlender" },
}, (obj_res) => { }, (obj_res) => {
if (obj_res.statusCode === 301 || obj_res.statusCode === 302) { if (obj_res.statusCode >= 300 && obj_res.statusCode < 400) {
obj_res.resume();
let str_redirect = obj_res.headers.location; let str_redirect = obj_res.headers.location;
if (!str_redirect) { if (!str_redirect) {
reject(new Error("Redirection sans header Location")); reject(new Error("Redirection sans header Location"));
@@ -224,6 +238,7 @@ const UpdateManager = {
} }
if (obj_res.statusCode !== 200) { if (obj_res.statusCode !== 200) {
obj_res.resume();
reject(new Error("Telechargement echoue : HTTP " + obj_res.statusCode)); reject(new Error("Telechargement echoue : HTTP " + obj_res.statusCode));
return; return;
} }
@@ -274,13 +289,8 @@ const UpdateManager = {
_extract_zip: (str_zip_path, str_dest_dir) => { _extract_zip: (str_zip_path, str_dest_dir) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (process.platform === "win32") { if (process.platform === "win32") {
let str_cmd = "Expand-Archive"; let str_script = "Expand-Archive -Path '" + str_zip_path + "' -DestinationPath '" + str_dest_dir + "' -Force";
let list_args = [ execFile("powershell.exe", ["-NoProfile", "-Command", str_script], (obj_err) => {
"-Path", str_zip_path,
"-DestinationPath", str_dest_dir,
"-Force",
];
execFile("powershell.exe", ["-Command", str_cmd + " " + list_args.join(" ")], (obj_err) => {
if (obj_err) { if (obj_err) {
reject(new Error("Extraction echouee : " + obj_err.message)); reject(new Error("Extraction echouee : " + obj_err.message));
return; return;
@@ -309,14 +319,34 @@ const UpdateManager = {
continue; continue;
} }
let str_full = path.join(str_temp_dir, str_entry); let str_full = path.join(str_temp_dir, str_entry);
if (fs.statSync(str_full).isDirectory()) { if (!fs.statSync(str_full).isDirectory()) {
continue;
}
let str_version_check = path.join(str_full, "version.json");
if (fs.existsSync(str_version_check)) {
str_root = str_full; str_root = str_full;
break; break;
} }
let list_sub = fs.readdirSync(str_full);
for (let str_sub of list_sub) {
let str_sub_full = path.join(str_full, str_sub);
if (fs.statSync(str_sub_full).isDirectory()) {
let str_nested_check = path.join(str_sub_full, "version.json");
if (fs.existsSync(str_nested_check)) {
str_root = str_sub_full;
break;
}
}
}
if (str_root) {
break;
}
} }
if (!str_root) { if (!str_root) {
reject(new Error("Dossier extrait introuvable")); reject(new Error("Dossier extrait introuvable (version.json absent)"));
return; return;
} }
@@ -326,15 +356,23 @@ const UpdateManager = {
_replace_app_files: (str_source_dir) => { _replace_app_files: (str_source_dir) => {
let str_app_dir = path.join(__dirname, "..", ".."); let str_app_dir = path.join(__dirname, "..", "..");
let LIST_TARGETS = ["main.js", "preload.js", "src", "version.json", "package.json"];
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
for (let str_required of LIST_REQUIRED_FILES) {
let str_check = path.join(str_source_dir, str_required);
if (!fs.existsSync(str_check)) {
reject(new Error("Fichier requis manquant dans la mise a jour : " + str_required));
return;
}
}
for (let str_target of LIST_TARGETS) { for (let str_target of LIST_TARGETS) {
let str_src = path.join(str_source_dir, str_target); let str_src = path.join(str_source_dir, str_target);
let str_dest = path.join(str_app_dir, str_target); let str_dest = path.join(str_app_dir, str_target);
if (!fs.existsSync(str_src)) { if (!fs.existsSync(str_src)) {
console.log("UpdateManager: cible absente dans la source, ignoree : " + str_target);
continue; continue;
} }

View File

@@ -92,7 +92,9 @@ const UpdateBanner = {
obj_btn_close.classList.add("d-none"); obj_btn_close.classList.add("d-none");
window.api.apply_update(UpdateBanner.str_pending_tag) window.api.apply_update(UpdateBanner.str_pending_tag)
.catch(() => {}); .catch((obj_err) => {
console.error("UpdateBanner: apply_update echoue :", obj_err);
});
}, },
_show_progress: (str_step, nb_percent) => { _show_progress: (str_step, nb_percent) => {