mirror of
git://f0xx.org/android_cast
synced 2026-07-29 07:39:15 +03:00
new stuff
This commit is contained in:
35
.cursor/rules/be-deploy-alerts.mdc
Normal file
35
.cursor/rules/be-deploy-alerts.mdc
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
description: Prominent BE/DB deploy alerts when crash reporter or backend changes need server action
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# BE and database deploy alerts
|
||||||
|
|
||||||
|
When your work touches the crash reporter backend, SQLite schema, nginx, or PHP on the BE (`apps.f0xx.org`), **always** end the relevant reply with a clearly separated block in **BIG CAPITAL LETTERS** if any of the following apply:
|
||||||
|
|
||||||
|
## Trigger — show the alert when
|
||||||
|
|
||||||
|
- **Database**: new/changed tables or columns (`schema.sqlite.sql`, `Database::ensureSchema`, `ALTER TABLE`, `tags_json`, `report_views`, migrations, or data-dir permissions).
|
||||||
|
- **BE deploy**: PHP/views/JS/CSS/config must be copied or synced to the Alpine tree under `android_cast/examples/crash_reporter/backend/`.
|
||||||
|
- **Hard reload on server**: nginx config fragment changed → `nginx -t` + reload; PHP-FPM restart after extension/config changes; opcache may need FPM restart.
|
||||||
|
- **Browser**: static assets (`app.js`, `app.css`) changed → user should hard-refresh after BE files are updated.
|
||||||
|
|
||||||
|
## Format (use exactly this style)
|
||||||
|
|
||||||
|
```
|
||||||
|
═══════════════════════════════════════════════════════════════
|
||||||
|
⚠️ ACTION REQUIRED ON BE / DB
|
||||||
|
═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
• DATABASE: … (what changed; auto-migrate on first request vs manual sqlite step)
|
||||||
|
• SYNC FILES: … (paths to copy)
|
||||||
|
• NGINX: … (reload if api locations changed)
|
||||||
|
• PHP-FPM: … (restart only if needed)
|
||||||
|
• BROWSER: HARD REFRESH after deploy
|
||||||
|
|
||||||
|
═══════════════════════════════════════════════════════════════
|
||||||
|
```
|
||||||
|
|
||||||
|
Use short bullet lines. If only one category applies, still use the banner and list only that item. If nothing requires BE/DB/nginx/browser action, do **not** show the banner.
|
||||||
|
|
||||||
|
Do not bury this in prose — the banner must be impossible to miss.
|
||||||
@@ -479,7 +479,30 @@ a:hover { text-decoration: underline; }
|
|||||||
}
|
}
|
||||||
.th-drag:active { cursor: grabbing; }
|
.th-drag:active { cursor: grabbing; }
|
||||||
.th-label { flex: 1; min-width: 0; }
|
.th-label { flex: 1; min-width: 0; }
|
||||||
.th-draggable.th-dragging { opacity: .55; }
|
.th-draggable.th-dragging { opacity: .45; }
|
||||||
|
.th-draggable.th-dragging .th-label {
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.data-table th.th-drop-target {
|
||||||
|
box-shadow: inset 0 -3px 0 var(--accent);
|
||||||
|
background: var(--row-hover);
|
||||||
|
}
|
||||||
|
.col-drag-ghost {
|
||||||
|
position: fixed;
|
||||||
|
top: -1000px;
|
||||||
|
left: -1000px;
|
||||||
|
z-index: 9999;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
box-shadow: 0 6px 18px rgba(0, 0, 0, .35);
|
||||||
|
pointer-events: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
.col-resizer {
|
.col-resizer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -548,20 +571,33 @@ a:hover { text-decoration: underline; }
|
|||||||
|
|
||||||
.reports-search-row {
|
.reports-search-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: nowrap;
|
||||||
align-items: flex-end;
|
align-items: center;
|
||||||
gap: 10px 14px;
|
justify-content: flex-end;
|
||||||
|
gap: 10px 12px;
|
||||||
|
width: 100%;
|
||||||
margin: 12px 0 8px;
|
margin: 12px 0 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.reports-search-label {
|
.reports-search-label {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.2;
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
.reports-search-field {
|
.reports-search-field {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1 1 280px;
|
flex: 0 0 40%;
|
||||||
min-width: 200px;
|
width: 40%;
|
||||||
|
max-width: 40%;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
.reports-search-row .btn {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
align-self: center;
|
||||||
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
.reports-search-input {
|
.reports-search-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -331,34 +331,79 @@
|
|||||||
cg.innerHTML = html;
|
cg.innerHTML = html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function columnLabelByKey(key) {
|
||||||
|
const c = LIST_COLUMNS.find((col) => col.key === key);
|
||||||
|
return c ? c.label : key;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearColumnDropMarkers(thead) {
|
||||||
|
thead.querySelectorAll('.th-drop-target').forEach((el) => {
|
||||||
|
el.classList.remove('th-drop-target');
|
||||||
|
});
|
||||||
|
thead.querySelectorAll('.th-dragging').forEach((el) => {
|
||||||
|
el.classList.remove('th-dragging');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function bindReportsTableLayout(table, thead, ctx) {
|
function bindReportsTableLayout(table, thead, ctx) {
|
||||||
if (!table || !thead || table.dataset.layoutBound === '1') return;
|
if (!table || !thead || table.dataset.layoutBound === '1') return;
|
||||||
table.dataset.layoutBound = '1';
|
table.dataset.layoutBound = '1';
|
||||||
let dragKey = null;
|
|
||||||
|
|
||||||
thead.addEventListener('dragstart', (e) => {
|
thead.addEventListener('dragstart', (e) => {
|
||||||
const handle = e.target.closest('.th-drag');
|
const handle = e.target.closest('.th-drag');
|
||||||
if (!handle || ctx.getGrouped()) return;
|
if (!handle || ctx.getGrouped()) return;
|
||||||
dragKey = handle.getAttribute('data-col-key');
|
const sourceKey = handle.getAttribute('data-col-key');
|
||||||
|
if (!sourceKey) return;
|
||||||
|
table.dataset.dragColKey = sourceKey;
|
||||||
const th = handle.closest('th');
|
const th = handle.closest('th');
|
||||||
if (th) th.classList.add('th-dragging');
|
if (th) th.classList.add('th-dragging');
|
||||||
|
const label = columnLabelByKey(sourceKey);
|
||||||
|
const ghost = document.createElement('div');
|
||||||
|
ghost.className = 'col-drag-ghost';
|
||||||
|
ghost.textContent = label;
|
||||||
|
ghost.setAttribute('aria-hidden', 'true');
|
||||||
|
document.body.appendChild(ghost);
|
||||||
|
table._dragGhost = ghost;
|
||||||
if (e.dataTransfer) {
|
if (e.dataTransfer) {
|
||||||
e.dataTransfer.effectAllowed = 'move';
|
e.dataTransfer.effectAllowed = 'move';
|
||||||
e.dataTransfer.setData('text/plain', dragKey || '');
|
e.dataTransfer.setData('text/plain', sourceKey);
|
||||||
|
try {
|
||||||
|
e.dataTransfer.setDragImage(ghost, 16, 14);
|
||||||
|
} catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
thead.addEventListener('dragend', (e) => {
|
thead.addEventListener('dragend', () => {
|
||||||
const th = e.target.closest('th');
|
clearColumnDropMarkers(thead);
|
||||||
if (th) th.classList.remove('th-dragging');
|
delete table.dataset.dragColKey;
|
||||||
dragKey = null;
|
if (table._dragGhost) {
|
||||||
|
table._dragGhost.remove();
|
||||||
|
table._dragGhost = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
thead.addEventListener('dragenter', (e) => {
|
||||||
|
if (ctx.getGrouped() || !table.dataset.dragColKey) return;
|
||||||
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
thead.addEventListener('dragover', (e) => {
|
thead.addEventListener('dragover', (e) => {
|
||||||
if (ctx.getGrouped()) return;
|
if (ctx.getGrouped() || !table.dataset.dragColKey) return;
|
||||||
if (!e.target.closest('th[data-col-key]')) return;
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
|
if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
|
||||||
|
const th = e.target.closest('th[data-col-key]');
|
||||||
|
if (!th) return;
|
||||||
|
const active = thead.querySelector('.th-drop-target');
|
||||||
|
if (active && active !== th) active.classList.remove('th-drop-target');
|
||||||
|
th.classList.add('th-drop-target');
|
||||||
|
});
|
||||||
|
|
||||||
|
thead.addEventListener('dragleave', (e) => {
|
||||||
|
const th = e.target.closest('th[data-col-key]');
|
||||||
|
if (!th) return;
|
||||||
|
const rel = e.relatedTarget;
|
||||||
|
if (rel && th.contains(rel)) return;
|
||||||
|
th.classList.remove('th-drop-target');
|
||||||
});
|
});
|
||||||
|
|
||||||
thead.addEventListener('drop', (e) => {
|
thead.addEventListener('drop', (e) => {
|
||||||
@@ -366,9 +411,18 @@
|
|||||||
const th = e.target.closest('th[data-col-key]');
|
const th = e.target.closest('th[data-col-key]');
|
||||||
if (!th) return;
|
if (!th) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
const sourceKey =
|
const sourceKey =
|
||||||
(e.dataTransfer && e.dataTransfer.getData('text/plain')) || dragKey;
|
table.dataset.dragColKey ||
|
||||||
|
(e.dataTransfer && e.dataTransfer.getData('text/plain')) ||
|
||||||
|
'';
|
||||||
const targetKey = th.getAttribute('data-col-key');
|
const targetKey = th.getAttribute('data-col-key');
|
||||||
|
clearColumnDropMarkers(thead);
|
||||||
|
delete table.dataset.dragColKey;
|
||||||
|
if (table._dragGhost) {
|
||||||
|
table._dragGhost.remove();
|
||||||
|
table._dragGhost = null;
|
||||||
|
}
|
||||||
if (!sourceKey || !targetKey || sourceKey === targetKey) return;
|
if (!sourceKey || !targetKey || sourceKey === targetKey) return;
|
||||||
const order = getOrderedColumnKeys();
|
const order = getOrderedColumnKeys();
|
||||||
const from = order.indexOf(sourceKey);
|
const from = order.indexOf(sourceKey);
|
||||||
@@ -377,7 +431,6 @@
|
|||||||
order.splice(from, 1);
|
order.splice(from, 1);
|
||||||
order.splice(to, 0, sourceKey);
|
order.splice(to, 0, sourceKey);
|
||||||
saveColumnOrder(order);
|
saveColumnOrder(order);
|
||||||
dragKey = null;
|
|
||||||
ctx.refreshLayout();
|
ctx.refreshLayout();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -907,7 +960,6 @@
|
|||||||
const searchSuggest = document.getElementById('reports-search-suggest');
|
const searchSuggest = document.getElementById('reports-search-suggest');
|
||||||
const searchClear = document.getElementById('reports-search-clear');
|
const searchClear = document.getElementById('reports-search-clear');
|
||||||
let suggestTimer = null;
|
let suggestTimer = null;
|
||||||
let searchRunTimer = null;
|
|
||||||
|
|
||||||
function hideSuggest() {
|
function hideSuggest() {
|
||||||
if (searchSuggest) {
|
if (searchSuggest) {
|
||||||
@@ -988,22 +1040,18 @@
|
|||||||
if (searchClear) searchClear.hidden = false;
|
if (searchClear) searchClear.hidden = false;
|
||||||
}
|
}
|
||||||
searchInput.addEventListener('input', () => {
|
searchInput.addEventListener('input', () => {
|
||||||
const q = searchInput.value.trim();
|
const q = searchInput.value;
|
||||||
if (searchClear) searchClear.hidden = !q;
|
if (searchClear) searchClear.hidden = !q.trim();
|
||||||
|
hideSuggest();
|
||||||
clearTimeout(suggestTimer);
|
clearTimeout(suggestTimer);
|
||||||
clearTimeout(searchRunTimer);
|
const trimmed = q.trim();
|
||||||
if (q.length < 2) {
|
if (trimmed.length < 2) return;
|
||||||
hideSuggest();
|
suggestTimer = setTimeout(() => fetchSuggest(trimmed), 1000);
|
||||||
return;
|
|
||||||
}
|
|
||||||
suggestTimer = setTimeout(() => fetchSuggest(q), 1000);
|
|
||||||
searchRunTimer = setTimeout(() => applySearchQuery(q, true), 1600);
|
|
||||||
});
|
});
|
||||||
searchInput.addEventListener('keydown', (e) => {
|
searchInput.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
clearTimeout(suggestTimer);
|
clearTimeout(suggestTimer);
|
||||||
clearTimeout(searchRunTimer);
|
|
||||||
hideSuggest();
|
hideSuggest();
|
||||||
applySearchQuery(searchInput.value.trim(), true);
|
applySearchQuery(searchInput.value.trim(), true);
|
||||||
}
|
}
|
||||||
@@ -1012,14 +1060,26 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function appendSearchTerm(term) {
|
||||||
|
if (!searchInput) return;
|
||||||
|
const t = (term || '').trim();
|
||||||
|
if (!t) return;
|
||||||
|
const cur = searchInput.value.trim();
|
||||||
|
if (!cur) {
|
||||||
|
searchInput.value = t;
|
||||||
|
} else if (!cur.toLowerCase().includes(t.toLowerCase())) {
|
||||||
|
searchInput.value = cur + ' ' + t;
|
||||||
|
}
|
||||||
|
if (searchClear) searchClear.hidden = !searchInput.value.trim();
|
||||||
|
searchInput.focus();
|
||||||
|
}
|
||||||
if (searchSuggest) {
|
if (searchSuggest) {
|
||||||
searchSuggest.addEventListener('click', (e) => {
|
searchSuggest.addEventListener('click', (e) => {
|
||||||
const btn = e.target.closest('[data-suggest]');
|
const btn = e.target.closest('[data-suggest]');
|
||||||
if (!btn) return;
|
if (!btn) return;
|
||||||
const term = btn.getAttribute('data-suggest') || '';
|
const term = btn.getAttribute('data-suggest') || '';
|
||||||
if (searchInput) searchInput.value = term;
|
appendSearchTerm(term);
|
||||||
hideSuggest();
|
hideSuggest();
|
||||||
applySearchQuery(term, true);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (searchClear) {
|
if (searchClear) {
|
||||||
|
|||||||
101
examples/crash_reporter/upload_test.sh
Executable file
101
examples/crash_reporter/upload_test.sh
Executable file
@@ -0,0 +1,101 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Batch-upload fake crash reports (java + native) from test_reports/.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
REPORTS_DIR="${REPORTS_DIR:-$ROOT/test_reports}"
|
||||||
|
BASE="${BASE:-https://apps.f0xx.org/app/androidcast_project/crashes}"
|
||||||
|
UPLOAD_URL="$BASE/api/upload.php"
|
||||||
|
JAR="${JAR:-/tmp/ac_crash_cookies.txt}"
|
||||||
|
LOGIN="${LOGIN:-0}"
|
||||||
|
DELAY_MS="${DELAY_MS:-50}"
|
||||||
|
ONLY="${ONLY:-all}" # all | java | native
|
||||||
|
|
||||||
|
if [[ ! -d "$REPORTS_DIR" ]]; then
|
||||||
|
echo "Missing $REPORTS_DIR — run: python3 $ROOT/generate_test_reports.py" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
shopt -s nullglob
|
||||||
|
FILES=()
|
||||||
|
case "$ONLY" in
|
||||||
|
java)
|
||||||
|
FILES=("$REPORTS_DIR"/crash_java_*.json)
|
||||||
|
;;
|
||||||
|
native)
|
||||||
|
FILES=("$REPORTS_DIR"/crash_native_*.json)
|
||||||
|
;;
|
||||||
|
all|*)
|
||||||
|
FILES=("$REPORTS_DIR"/crash_java_*.json "$REPORTS_DIR"/crash_native_*.json)
|
||||||
|
# legacy single-prefix files from older generator
|
||||||
|
LEGACY=("$REPORTS_DIR"/crash_[0-9][0-9][0-9].json)
|
||||||
|
if [[ ${#FILES[@]} -eq 0 && ${#LEGACY[@]} -gt 0 ]]; then
|
||||||
|
FILES=("${LEGACY[@]}")
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shopt -u nullglob
|
||||||
|
|
||||||
|
if [[ ${#FILES[@]} -eq 0 ]]; then
|
||||||
|
echo "No reports in $REPORTS_DIR (expected crash_java_*.json / crash_native_*.json)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
IFS=$'\n' FILES=($(printf '%s\n' "${FILES[@]}" | sort))
|
||||||
|
unset IFS
|
||||||
|
|
||||||
|
java_n=0
|
||||||
|
native_n=0
|
||||||
|
for f in "${FILES[@]}"; do
|
||||||
|
case "$(basename "$f")" in
|
||||||
|
crash_java_*) ((java_n++)) || true ;;
|
||||||
|
crash_native_*) ((native_n++)) || true ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$LOGIN" == "1" ]]; then
|
||||||
|
curl -sk -c "$JAR" -b "$JAR" -L -X POST "$BASE/login" \
|
||||||
|
-d 'username=admin&password=admin' -o /dev/null
|
||||||
|
echo "Logged in (cookie jar: $JAR)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ok=0
|
||||||
|
fail=0
|
||||||
|
dup=0
|
||||||
|
ok_java=0
|
||||||
|
ok_native=0
|
||||||
|
|
||||||
|
echo "Uploading ${#FILES[@]} reports to $UPLOAD_URL (java=$java_n native=$native_n only=$ONLY)"
|
||||||
|
|
||||||
|
for f in "${FILES[@]}"; do
|
||||||
|
name="$(basename "$f")"
|
||||||
|
body="$(curl -sk -w '\n%{http_code}' -H 'Content-Type: application/json' --data-binary "@$f" "$UPLOAD_URL")"
|
||||||
|
code="$(echo "$body" | tail -n1)"
|
||||||
|
json="$(echo "$body" | sed '$d')"
|
||||||
|
|
||||||
|
case "$code" in
|
||||||
|
200)
|
||||||
|
((ok++)) || true
|
||||||
|
case "$name" in
|
||||||
|
crash_java_*) ((ok_java++)) || true; echo " OK [java] $name" ;;
|
||||||
|
crash_native_*) ((ok_native++)) || true; echo " OK [native] $name" ;;
|
||||||
|
*) echo " OK $name" ;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
409)
|
||||||
|
((dup++)) || true
|
||||||
|
echo " DUP $name (report_id already stored)"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
((fail++)) || true
|
||||||
|
echo " FAIL $name HTTP $code — $json" >&2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ "$DELAY_MS" -gt 0 ]]; then
|
||||||
|
sleep "$(awk "BEGIN { print $DELAY_MS / 1000 }")"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Done: ok=$ok (java=$ok_java native=$ok_native) duplicate=$dup failed=$fail (total=${#FILES[@]})"
|
||||||
|
[[ "$fail" -eq 0 ]]
|
||||||
38
mount_be_alpine.sh
Executable file
38
mount_be_alpine.sh
Executable file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
## FE - frontend mounts
|
||||||
|
[[ -z "$(mount | grep FE | grep f0xx)" ]] && sshfs foxx@f0xx.org:/etc/nginx/ tmp/FE_gentoo/etc/nginx/ -o -p 222
|
||||||
|
[[ -d "tmp/FE_gentoo/var/www/localhost/htdocs/android_cast/" && -d "tmp/FE_gentoo/var/www/localhost/htdocs/android_cast/" && -z "$(mount | grep FE | grep f0xx)" ]] && \
|
||||||
|
|
||||||
|
#sleep 5
|
||||||
|
## BE - backend - through jump server through FE
|
||||||
|
[ -z "${ARTC0_HOST}" ] && ARTC0_HOST="10.7.16.128"
|
||||||
|
#ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o ProxyJump=foxx@f0xx.org -P 222 foxx@${ARTC0_HOST}
|
||||||
|
|
||||||
|
## check out the changes in the ~/.ssh/config
|
||||||
|
## and
|
||||||
|
## ssh -o 'ProxyJump=foxx@f0xx.org:222' 'foxx@10.7.16.128'
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
if [[ -z "$(mount | grep BE | grep etc | grep f0xx)" && -d "tmp/BE_alpine/etc/nginx" ]]; then
|
||||||
|
set -x
|
||||||
|
sshfs alpine-be:/etc/nginx tmp/BE_alpine/etc/nginx
|
||||||
|
set +x
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
if [[ -z "$(mount | grep BE | grep var | grep f0xx)" && -d "tmp/BE_alpine/var/www/localhost/htdocs/apps" ]]; then
|
||||||
|
set -x
|
||||||
|
sshfs alpine-be:/var/www/localhost/htdocs/apps tmp/BE_alpine/var/www/localhost/htdocs/apps
|
||||||
|
set +x
|
||||||
|
fi
|
||||||
|
|
||||||
|
_umount() {
|
||||||
|
umount "${1}"
|
||||||
|
}
|
||||||
|
|
||||||
|
#_umount_fe {
|
||||||
|
# fe_eps=$(mount | grep FE | grep -e f0xx -w FE_gentoo )
|
||||||
|
#}
|
||||||
|
|
||||||
Reference in New Issue
Block a user