Merge branch 'main' into build-package

This commit is contained in:
hansrachit123 2025-09-15 17:13:31 -03:00
commit c633b76e3e
4 changed files with 104 additions and 40 deletions

View File

@ -2,7 +2,7 @@ name: Build Packages
on: on:
push: push:
branches: branches:
- build-package - build
tags: tags:
- 'v*.*.*' - 'v*.*.*'
jobs: jobs:
@ -107,4 +107,4 @@ jobs:
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
uses: 45drives/actions/sync-repo@main uses: 45drives/actions/sync-repo@main
with: with:
directory: ${{ github.workspace }} directory: ${{ github.workspace }}

View File

@ -5,7 +5,7 @@
"description": "A File System Browser for Cockpit.", "description": "A File System Browser for Cockpit.",
"version": "0.5.10", "version": "0.5.10",
"build_number": "1", "build_number": "1",
"stable": true, "stable": false,
"author": "Josh Boudreau <jboudreau@45drives.com>", "author": "Josh Boudreau <jboudreau@45drives.com>",
"git_url": "https://github.com/45Drives/cockpit-navigator", "git_url": "https://github.com/45Drives/cockpit-navigator",
"license": "GPL-3.0+", "license": "GPL-3.0+",
@ -14,8 +14,7 @@
}, },
"architecture": { "architecture": {
"rocky": "x86_64", "rocky": "x86_64",
"debian": "amd64", "ubuntu": "all"
"ubuntu": "amd64"
}, },
"dependencies": { "dependencies": {
"ubuntu_common": [ "ubuntu_common": [
@ -24,7 +23,8 @@
"rsync", "rsync",
"zip", "zip",
"file", "file",
"coreutils" "coreutils",
"inotify-tools"
], ],
"rocky_common": [ "rocky_common": [
"cockpit", "cockpit",
@ -33,7 +33,8 @@
"zip", "zip",
"file", "file",
"/bin/mkdir", "/bin/mkdir",
"/bin/rmdir" "/bin/rmdir",
"inotify-tools"
] ]
}, },
"builds": [ "builds": [

View File

@ -100,46 +100,96 @@ export class NavContextMenu {
} }
e.stopPropagation(); e.stopPropagation();
} }
zip_for_download() { zip_for_download() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
var cmd = [ const cmd = ["/usr/share/cockpit/navigator/scripts/zip-for-download.py3",
"/usr/share/cockpit/navigator/scripts/zip-for-download.py3", this.nav_window_ref.pwd().path_str()];
this.nav_window_ref.pwd().path_str() for (const entry of this.nav_window_ref.selected_entries) cmd.push(entry.path_str());
]; const proc = cockpit.spawn(cmd, { superuser: "try", err: "out" });
for (let entry of this.nav_window_ref.selected_entries) {
cmd.push(entry.path_str()); const safeParse = (raw) => {
const s = (raw || "").trim();
const start = s.indexOf("{");
const end = s.lastIndexOf("}");
if (start === -1 || end === -1 || end < start) {
throw new Error("No JSON object in output: " + s.slice(0, 200));
} }
var proc = cockpit.spawn(cmd, {superuser: "try", err: "out"}); return JSON.parse(s.slice(start, end + 1));
proc.fail((e, data) => { };
reject(JSON.parse(data));
}); proc.done((data) => {
proc.done((data) => {
resolve(JSON.parse(data));
});
});
}
async download(e) {
var download_target = "";
if (this.nav_window_ref.selected_entries.size === 1 && !(this.nav_window_ref.selected_entry() instanceof NavDir)) {
download_target = this.nav_window_ref.selected_entry();
} else {
this.nav_window_ref.start_load();
var result;
try { try {
result = await this.zip_for_download(); resolve(safeParse(data));
} catch(e) { } catch (e) {
this.nav_window_ref.stop_load(); console.error("zip_for_download done(raw):", data);
this.nav_window_ref.modal_prompt.alert(e.message); reject({ message: e.message });
return;
} }
this.nav_window_ref.stop_load(); });
proc.fail((e, data) => {
try {
reject(safeParse(data));
} catch {
console.error("zip_for_download fail(raw):", data);
reject({ message: String(data || e || "zip_for_download failed") });
}
});
});
}
async download(e) {
let download_target = "";
let result; // function-scoped so we can reference later
if (this.nav_window_ref.selected_entries.size === 1 &&
!(this.nav_window_ref.selected_entry() instanceof NavDir)) {
download_target = this.nav_window_ref.selected_entry();
} else {
this.nav_window_ref.start_load();
try {
result = await this.zip_for_download();
download_target = new NavFile(result["archive-path"], result["stat"], this.nav_window_ref); download_target = new NavFile(result["archive-path"], result["stat"], this.nav_window_ref);
console.log("prepared archive for download:", result["archive-path"]);
} catch (err) {
this.nav_window_ref.stop_load();
this.nav_window_ref.modal_prompt.alert(err.message);
return;
} finally {
this.nav_window_ref.stop_load();
}
} }
var download = new NavDownloader(download_target); if (result?.["archive-path"]) {
download.download(); const unitName = `nav-clean-on-open-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;
} const script = [
'set -euo pipefail',
'if ! command -v inotifywait >/dev/null 2>&1; then ' +
'sleep 300; rm -f -- "$ARCHIVE"; ' +
'[ -n "${TEMPDIR:-}" ] && [[ "$TEMPDIR" == /tmp/navigator-* ]] && rm -rf -- "$TEMPDIR"; ' +
'exit 0; fi',
'inotifywait -q -t 1800 -e open -- "$ARCHIVE" || true',
'rm -f -- "$ARCHIVE"',
'sleep 3600',
'[ -n "${TEMPDIR:-}" ] && [[ "$TEMPDIR" == /tmp/navigator-* ]] && rm -rf -- "$TEMPDIR" || :'
].join(' && ');
const cmd = [
'systemd-run',
'--property=CollectMode=inactive-or-failed',
'--property=RuntimeMaxSec=90000',
'--unit', unitName,
'--setenv=ARCHIVE=' + result['archive-path'],
...(result['temp-dir'] ? ['--setenv=TEMPDIR=' + result['temp-dir']] : []),
'/bin/bash','-lc', script
];
await cockpit.spawn(cmd, { superuser: 'require', err: 'out' }).then(out => console.log(out));
console.log("scheduled cleanup:", unitName);
console.log("Deleting :", result['archive-path'], "in 30 minutes or after download starts.");
}
const downloader = new NavDownloader(download_target);
downloader.download();
}
delete(e) { delete(e) {
this.nav_window_ref.delete_selected(); this.nav_window_ref.delete_selected();

View File

@ -331,6 +331,19 @@ export class NavWindow {
} }
update_selection_info() { update_selection_info() {
if (this.selected_entries.size > 1) {
// get first element of the Set
const it = this.selected_entries.values();
const first = it.next().value;
const second = it.next().value;
if(first.path.length<second.path.length){
this.selected_entries.delete(first)
}
if(first.path.length==second.path.length && first.filename=="/"){
this.selected_entries.delete(first)
}
}
if (this.selected_entries.size > 1){ if (this.selected_entries.size > 1){
var name_fields = document.getElementsByClassName("nav-info-column-filename"); var name_fields = document.getElementsByClassName("nav-info-column-filename");
for (let name_field of name_fields) { for (let name_field of name_fields) {