From 9a265bf2272fb39c2409d9d9a0566ec6c15f1fb0 Mon Sep 17 00:00:00 2001 From: IFOKNR <253658289+ifoknr@users.noreply.github.com> Date: Thu, 15 Oct 2026 00:15:19 +0000 Subject: [PATCH] v2.5.1: first stable release, and backups can be deleted from the phone - FBS_CHANNEL is stable: module.prop drops (Beta), the WebUI hides its Beta chip and release.yml no longer forces a pre-release. - fbs delete removes one finished backup, only directly under Backups and never a hidden .partial- folder. The Backup tab has a Delete button with a confirmation. - Tests for delete, and the WebUI smoke test checks a stable build shows no Beta chip. --- CHANGELOG.md | 10 ++++++++++ README.ar.md | 9 ++++++--- README.md | 9 ++++++--- module/bin/fbs | 6 ++++-- module/lib/backup.sh | 22 ++++++++++++++++++++++ module/module.prop | 8 ++++---- module/webroot/app.js | 18 +++++++++++++++++- module/webroot/demo.js | 10 ++++++++-- module/webroot/i18n.js | 4 ++++ module/webroot/style.css | 8 +++++--- tests/run.sh | 14 ++++++++++++++ tests/webui-smoke.sh | 3 ++- update.json | 6 +++--- 13 files changed, 105 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63ffeda..0bd803f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ +## v2.5.1 +- أول إصدار رسمي: الإضافة طلعت من التجريبي (Beta) +- زر «حذف» لكل نسخة احتياطية في تبويب الباك أب، مع تأكيد قبل الحذف +- أمر fbs delete يحذف نسخة وحدة من مجلد Backups بس + +**English** +- First stable release: the module is out of beta +- A Delete button for each backup in the Backup tab, with a confirmation first +- fbs delete removes one backup, and only inside the Backups folder + ## v2.5.0 - متوافق مع صفحة «الجوال» في Fastboot Studio v2.5.0: المواصفات والحماية والتعارضات والنسخ الاحتياطية - النسخة الاحتياطية تنكتب في مجلد مخفي وما يطلع اسمها إلا لما تكتمل، فالبرنامج ما ينقل نسخة ناقصة diff --git a/README.ar.md b/README.ar.md index 42faa7c..4aaedaf 100644 --- a/README.ar.md +++ b/README.ar.md @@ -13,7 +13,7 @@ Magisk | KernelSU | APatch Languages Latest release - Beta + Stable License MIT Telegram group

@@ -26,8 +26,11 @@ **للقراءة فقط.** ما يشتغل شي وقت الإقلاع (لا `service.sh` ولا `post-fs-data.sh`)، فما يسبب بوت لوب، وما يكتب على أي بارتشن. الكتابة الوحيدة هي النسخ الاحتياطية والتقارير في `/sdcard/FastbootStudio`. -> 🧪 **تجريبي.** الوحدة في مرحلة تجريبية، وأرقام إصداراتها تمشي مع أرقام Fastboot Studio، -> وإصداراتها تنزل كإصدارات تجريبية لين تصير مستقرة. +> ✅ **مستقرة من 2.5.1.** وأرقام إصداراتها تمشي مع أرقام Fastboot Studio. +> +> **الجديد في 2.5.1:** +> - طلعت من التجريبي: أول إصدار رسمي. +> - تحذف النسخة الاحتياطية من الجوال مباشرة من تبويب الباك أب (`fbs delete`). > > **الجديد في 2.5:** > - متوافقة مع صفحة **الجوال** في Fastboot Studio 2.5: المواصفات والحماية والتعارضات والنسخ الاحتياطية. diff --git a/README.md b/README.md index daf5066..4e12052 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Magisk | KernelSU | APatch Languages Latest release - Beta + Stable License MIT Telegram group

@@ -25,8 +25,11 @@ its partitions in the same layout the Windows app uses, all from the phone itsel cannot cause a bootloop, and it never writes to a partition. The only writes are backups and reports under `/sdcard/FastbootStudio`. -> 🧪 **Beta.** The module is experimental. Its version numbers go along with Fastboot Studio's, -> and releases are published as pre-releases until it is marked stable. +> ✅ **Stable since 2.5.1.** Its version numbers go along with Fastboot Studio's. +> +> **New in 2.5.1:** +> - Out of beta: the first stable release. +> - Delete a backup from the phone right in the Backup tab (`fbs delete`). > > **New in 2.5:** > - Works with the **Phone** page in Fastboot Studio 2.5: specs, security, conflicts and backups. diff --git a/module/bin/fbs b/module/bin/fbs index 801f070..01f56f8 100755 --- a/module/bin/fbs +++ b/module/bin/fbs @@ -5,10 +5,10 @@ # (or one JSON object per line for long jobs), so all three always agree. Nothing here writes # to a partition: the only writes are backups and reports under /sdcard/FastbootStudio. -FBS_VERSION=2.5.0 +FBS_VERSION=2.5.1 # beta while the module is experimental; stable once it is not. release.yml publishes a beta # as a pre-release, the WebUI shows a Beta badge, and tools/build.sh checks module.prop agrees. -FBS_CHANNEL=beta +FBS_CHANNEL=stable FBS_LIB=${FBS_LIB:-${0%/*}/../lib} # shellcheck source=SCRIPTDIR/../lib/common.sh @@ -44,6 +44,7 @@ fbs $FBS_VERSION ($FBS_CHANNEL), Fastboot Studio Companion backups the backups on the phone (JSON) modules installed modules and the conflicts between them (JSON) verify re-check a backup against its SHA256SUMS (JSON lines) + delete remove one backup from the phone (JSON) reboot bootloader|fastboot|recovery|edl bundle [plain] logs and device info for a bug report, IMEI and serial masked open open a link in the app that owns it (Telegram, the browser) @@ -66,6 +67,7 @@ case $cmd in backups) backups_json ;; modules) modules_json ;; verify) verify_run "$@" ;; + delete) delete_run "$@" ;; reboot) reboot_to "$@" ;; bundle) bundle_run "$@" ;; open) open_url "$@" ;; diff --git a/module/lib/backup.sh b/module/lib/backup.sh index d3269bb..8c8d890 100644 --- a/module/lib/backup.sh +++ b/module/lib/backup.sh @@ -165,6 +165,28 @@ backups_json() { printf ']}\n' } +# fbs delete : removes one finished backup. Only a folder directly under Backups, +# never an unfinished .partial- one (the running backup cleans those up itself). +delete_run() { + case $1 in + '' | */* | .*) + echo '{"ok":false,"msg":"usage: fbs delete "}' + return 2 + ;; + esac + _out=$DATA/Backups/$1 + if [ ! -d "$_out" ]; then + echo '{"ok":false,"msg":"missing"}' + return 1 + fi + if rm -rf "$_out"; then + printf '{"ok":true,"folder":%s}\n' "$(js "$1")" + else + echo '{"ok":false,"msg":"storage"}' + return 1 + fi +} + # fbs verify : re-hashes every file listed in SHA256SUMS. verify_run() { case $1 in diff --git a/module/module.prop b/module/module.prop index f55ee8d..993f31e 100644 --- a/module/module.prop +++ b/module/module.prop @@ -1,7 +1,7 @@ id=fastboot_studio_companion -name=Fastboot Studio Companion (Beta) -version=v2.5.0 -versionCode=250 +name=Fastboot Studio Companion +version=v2.5.1 +versionCode=251 author=IFOKNR -description=Beta. Device check, partitions, live kernel log and SHA-256 backups for Fastboot Studio. Read-only, nothing runs at boot. Open the WebUI or press Action. +description=Device check, partitions, live kernel log and SHA-256 backups for Fastboot Studio. Read-only, nothing runs at boot. Open the WebUI or press Action. updateJson=https://raw.githubusercontent.com/ifoknr/FastbootStudio-Companion/main/update.json diff --git a/module/webroot/app.js b/module/webroot/app.js index c9a6d5b..9b9098f 100644 --- a/module/webroot/app.js +++ b/module/webroot/app.js @@ -590,7 +590,8 @@ async function loadBackups() { return; } list.innerHTML = b.items.map(x => `
${esc(x.name)}/` + - `` + + `` + + `` + `${esc(T('backup.files', x.files, x.kb == null ? '–' : size(x.kb * 1024)))}
`).join(''); } catch (e) { list.innerHTML = `

${esc(T('err.generic', e.message || e))}

`; @@ -601,6 +602,7 @@ $('#backupList').addEventListener('click', async e => { const btn = e.target.closest('button'); if (!btn || btn.disabled) return; const item = btn.closest('.bitem'); + if (btn.dataset.act === 'delete') return askDelete(item.dataset.name); const meta = item.querySelector('.m'); btn.disabled = true; btn.textContent = T('backup.verifying'); @@ -622,6 +624,20 @@ $('#backupList').addEventListener('click', async e => { : `✓ ${esc(T('backup.ok', result.ok))}`; }); +function askDelete(name) { + sheet(T('backup.deleteTitle'), `

${esc(T('backup.deleteBody', name))}

`, + [{ label: T('cancel') }, { label: T('backup.delete'), kind: 'danger', onClick: async () => { + closeSheet(); + try { + const r = await fbsJson('delete', name); + toast(r.ok ? T('backup.deleted') : T('err.generic', r.msg)); + } catch (e) { + toast(T('err.generic', e.message || e)); + } + loadBackups(); + } }]); +} + /* ---------------------------------------------------------------- modules */ const Mods = { data: null, busy: false }; diff --git a/module/webroot/demo.js b/module/webroot/demo.js index 9eb93de..702aef6 100644 --- a/module/webroot/demo.js +++ b/module/webroot/demo.js @@ -22,7 +22,7 @@ ]; const info = { - fbs: '2.5.0', channel: 'beta', model: 'Demo Phone', brand: 'Demo', device: 'demo', product: 'demo_mt6789', + fbs: '2.5.1', channel: 'stable', model: 'Demo Phone', brand: 'Demo', device: 'demo', product: 'demo_mt6789', android: '14', sdk: '34', patch: '2026-08-05', build: 'DEMO-14.0.1', platform: 'mt6789', soc: 'mediatek', arch: 'arm64-v8a', slot: '_a', dynamic: true, treble: true, vndk: '34', boot: { vbstate: 'orange', device_state: 'unlocked', locked: false, spoofed: true }, @@ -123,6 +123,12 @@ case 'reboot': return JSON.stringify({ ok: true }); case 'open': return JSON.stringify({ ok: true }); case 'modules': return JSON.stringify(modules); + case 'delete': { + const i = backups.findIndex(x => x.name === a1); + if (i < 0) return JSON.stringify({ ok: false, msg: 'missing' }); + backups.splice(i, 1); + return JSON.stringify({ ok: true, folder: a1 }); + } default: return ''; } } @@ -152,7 +158,7 @@ const modules = { root: '/data/adb/modules', modules: [ - m('fastboot_studio_companion', 'Fastboot Studio Companion (Beta)', 'v2.5.0', { webui: true, action: true }), + m('fastboot_studio_companion', 'Fastboot Studio Companion', 'v2.5.1', { webui: true, action: true }), m('systemless_hosts', 'Systemless Hosts', 'v1.2', { files: 1 }), m('adblock_plus', 'AdBlock Plus', 'v4.0', { files: 1, props: 2, scripts: ['service.sh'] }), m('google_sans_font', 'Google Sans Font', 'v3.1', { files: 46 }), diff --git a/module/webroot/i18n.js b/module/webroot/i18n.js index 0bd36fa..60bbc20 100644 --- a/module/webroot/i18n.js +++ b/module/webroot/i18n.js @@ -52,6 +52,8 @@ const STRINGS = { 'backup.start': 'Start backup', 'backup.running': 'Backing up…', 'backup.saved': 'Saved backups', 'backup.format': 'Same layout as Fastboot Studio on Windows: its Phone page (v2.5.0 and later) copies a backup to your PC and checks it in one click, and the Device page restores it. Or check it here with sha256sum -c.', 'backup.none': 'No backups yet.', 'backup.verify': 'Verify', 'backup.verifying': 'Checking…', + 'backup.delete': 'Delete', 'backup.deleteTitle': 'Delete this backup?', 'backup.deleted': 'Backup deleted', + 'backup.deleteBody': '{0} is removed from the phone for good. Copy it to your PC first if you still need it.', 'backup.ok': 'All {0} match', 'backup.bad': '{0} changed or missing', 'backup.files': '{0} files · {1}', 'backup.done': 'Saved {0} images', 'backup.failed': '{0} failed', 'backup.confirm': 'Full backup', 'backup.confirmBody': 'This copies {0} partitions ({1}) to {2}. It can take several minutes.', @@ -125,6 +127,8 @@ const STRINGS = { 'backup.start': 'ابدأ الباك أب', 'backup.running': 'جاري النسخ…', 'backup.saved': 'النسخ المحفوظة', 'backup.format': 'نفس صيغة Fastboot Studio في الويندوز: صفحة «الجوال» في البرنامج (v2.5.0 وأحدث) تنقل النسخة للكمبيوتر وتتحقق منها بضغطة، وتسترجعها من صفحة الجهاز. أو تحقق منها هنا بـ sha256sum -c.', 'backup.none': 'ما فيه نسخ للحين.', 'backup.verify': 'تحقق', 'backup.verifying': 'يتحقق…', + 'backup.delete': 'حذف', 'backup.deleteTitle': 'تحذف هالنسخة؟', 'backup.deleted': 'انحذفت النسخة', + 'backup.deleteBody': '{0} بتنحذف من الجوال نهائياً. انقلها للكمبيوتر أول إذا تحتاجها.', 'backup.ok': 'كلها مطابقة ({0})', 'backup.bad': '{0} متغيّر أو ناقص', 'backup.files': '{0} ملف · {1}', 'backup.done': 'انحفظت {0} صورة', 'backup.failed': '{0} فشلت', 'backup.confirm': 'باك أب كامل', 'backup.confirmBody': 'بينسخ {0} بارتشن ({1}) إلى {2}. ممكن ياخذ كم دقيقة.', diff --git a/module/webroot/style.css b/module/webroot/style.css index 728b428..0282676 100644 --- a/module/webroot/style.css +++ b/module/webroot/style.css @@ -127,6 +127,7 @@ main { padding: 1rem 1rem calc(var(--bottom) + 6rem); max-width: 1500px; margin: .fbtn.rules { margin-inline-start: auto; border-color: var(--info-line); color: var(--info); background: var(--info-soft); } .btn-s { background: var(--raised); border: 1px solid var(--line); border-radius: .7rem; padding: .55rem .9rem; cursor: pointer; font-size: .9rem; color: var(--dim); white-space: nowrap; } .btn-s[aria-pressed="true"] { color: var(--warn); border-color: var(--warn-line); background: var(--warn-soft); } +.btn-s.danger { color: var(--danger); border-color: var(--danger-line); } .seg { display: grid; grid-template-columns: 1fr 1fr; background: var(--raised); border: 1px solid var(--line); border-radius: .7rem; padding: .2rem; gap: .2rem; } .seg.three { grid-template-columns: 1fr 1fr 1fr; } .seg button { background: none; border: 0; border-radius: .55rem; padding: .5rem; font-size: .9rem; cursor: pointer; color: var(--dim); } @@ -190,11 +191,12 @@ main { padding: 1rem 1rem calc(var(--bottom) + 6rem); max-width: 1500px; margin: .prow .st.ok { color: var(--ok); } .prow .st.bad { color: var(--danger); } .blist { display: flex; flex-direction: column; gap: .8rem; } -.bitem { display: grid; grid-template-columns: 1fr auto; gap: .15rem .5rem; align-items: center; font-size: .9rem; } +.bitem { display: grid; grid-template-columns: 1fr auto auto; gap: .15rem .5rem; align-items: center; font-size: .9rem; } .bitem .p { font-family: var(--mono); font-size: .8rem; direction: ltr; unicode-bidi: isolate; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; } [dir="rtl"] .bitem .p { text-align: right; } -.bitem .m { color: var(--faint); font-size: .8rem; } -.bitem button { grid-row: span 2; } +.bitem .m { color: var(--faint); font-size: .8rem; grid-column: 1; grid-row: 2; } +.bitem .p { grid-column: 1; grid-row: 1; } +.bitem button { grid-row: 1 / span 2; } /* Links to the Windows app, the group and a private message. */ .links { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: .5rem; margin-top: .9rem; } diff --git a/tests/run.sh b/tests/run.sh index c6d1577..28f63fd 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -177,6 +177,20 @@ for SHN in $SHELLS; do out=$(fbs verify ".partial-$name") expect "verify skips unfinished backups" jqt "$out" '.event == "error"' + # ---- delete + out=$(fbs delete ../etc) + expect "delete stays inside Backups" jqt "$out" '.ok == false' + expect "delete leaves other folders alone" test -d "$WORK/data/Backups" + out=$(fbs delete ".partial-x") + expect "delete refuses hidden folders" jqt "$out" '.ok == false' + out=$(fbs delete "no-such-backup") + expect "delete reports a missing backup" jqt "$out" '.ok == false and .msg == "missing"' + out=$(fbs delete "$name") + expect "delete removes the backup" jqt "$out" '.ok == true' + expect "deleted folder is gone" test ! -e "$dir" + out=$(fbs backups) + expect "two backups left" jqt "$out" '.items | length == 2' + # ---- reboot rm -f "$R/reboot.log" fbs reboot bootloader > /dev/null diff --git a/tests/webui-smoke.sh b/tests/webui-smoke.sh index 41551dd..9ee4511 100755 --- a/tests/webui-smoke.sh +++ b/tests/webui-smoke.sh @@ -21,7 +21,8 @@ for lang in en ar; do check "super card filled" 'system_a' check "backup sets described" 'id="setCritical">[^<]' check "reboot targets listed" 'data-r="bootloader"' - check "beta badge shown" 'id="betaChip" data-i="beta">' + check "no beta badge on a stable build" 'id="betaChip" hidden' + check "backups can be deleted" 'data-act="delete"' check "links listed" 'data-url="https://t.me/IFOKNR1"' check "group link" 'data-url="https://t.me/BeNeXTBrO"' check "link to the Windows app" 'data-url="https://github.com/ifoknr/FastbootStudio/releases/latest"' diff --git a/update.json b/update.json index 1b796ed..07d3a1f 100644 --- a/update.json +++ b/update.json @@ -1,6 +1,6 @@ { - "version": "v2.5.0", - "versionCode": 250, - "zipUrl": "https://github.com/ifoknr/FastbootStudio-Companion/releases/download/v2.5.0/FastbootStudio-Companion-v2.5.0.zip", + "version": "v2.5.1", + "versionCode": 251, + "zipUrl": "https://github.com/ifoknr/FastbootStudio-Companion/releases/download/v2.5.1/FastbootStudio-Companion-v2.5.1.zip", "changelog": "https://raw.githubusercontent.com/ifoknr/FastbootStudio-Companion/main/CHANGELOG.md" }