dartssh2's SftpFileWriter leaks chunk write failures as unhandled exceptions and never completes when the server rejects data (e.g. a full /tmp), so deploys hung and spammed the console. Upload in windowed writeBytes batches instead so errors propagate, remove the partial file, and report free space on the target directory. Every deploy flow now removes its staging files in a finally block (success, failure or cancel) and sweeps peachdeploy-*/peachweb-* leftovers older than an hour from earlier crashed runs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PeachDeployments 🚀
A cross-platform jar deployment cockpit. Register a server, add the services
that run on it, drag in a .jar, and PeachDeployments safely uploads it, swaps it for
the running one, restarts the systemd service, and streams the logs back — with
one-click revert if the new build fails to start.
Runs on Windows, macOS, Linux, Android and iOS (Flutter). SSH/SFTP uses raw sockets, so it does not run on Flutter Web.
Model: Server → Services
- A Server is a unique machine: name, host, port, username, password, sudo.
- Each server holds many Services, of two kinds:
- Jar service — a systemd unit + jar (deploy to a directory, restart).
- Web app — an Angular / React / Flutter-web / static site published to an nginx web root.
So one server profile can host orders-api (jar), marketing-site (React),
admin (Angular) … each deployed independently.
What it does
- Web apps → nginx — build Angular/React/Flutter-web (or use a pre-built
folder), packaged into a single gzip bundle for a fast upload, extracted
into the nginx web root with a safe backup, then
nginx -t+ reload — with revert on failure. - Two ways to ship, then confirm — on a service you either drag the artifact (jar / built folder) or point at the source; building runs locally, copies the output, and then asks you to proceed to deploy.
- Live step tracker & transfer stats — every build/deploy shows an ordered stepper with per-step state, and uploads show a descriptive panel with %, transferred, speed and ETA.
- Cancel an in-flight build or deploy; restart a service / reload nginx without deploying; and view logs without deploying (per service).
- Distinct success state — after a successful deploy the panel shows a DEPLOYED ✓ badge and only a subdued Redeploy (no accidental re-runs).
- Home dashboard — list/grid toggle, a metrics panel (success-rate ring + totals) and a Recently deployed feed.
- Build from source & deploy — give a service the path to its local
Maven/Gradle project and hit Build & deploy from source. PeachDeployments
auto-detects the build tool (
pom.xml→mvn -DskipTests package,build.gradle[.kts]→gradlew build -x test, preferring the wrapper), streams the build output into the console, locates the runnable jar (ignoring-sources/-javadoc/-plain/original-artifacts), then runs the full safe deploy below. Optional custom build command and jar pattern per service. (Local builds run on desktop only.) - Encrypted credentials — passwords are stored in the OS keystore via
flutter_secure_storage(Keychain / Android Keystore / Windows DPAPI / libsecret), never in plain config. - Drag & drop (or browse) a
.jaron a service's deploy screen. - Safe deployment pipeline:
- Upload the new jar to a temp path (
/tmp) — the running jar is untouched until the transfer fully succeeds. - Stop the service.
- Back up the current jar to
<name>.bak(old backup removed first). - Install the new jar under the configured name / path.
- Start the service and verify it is
active. - On success → delete the
.bak. On failure → keep the.bakand new jar, and offer Revert (restore previous jar, delete the failed one, restart) or Keep new.
- Upload the new jar to a temp path (
- Live logs — recent
journalctlafter each deploy, plus a Live tail toggle that follows the service journal in real time. - SFTP file browser — per server: navigate directories, upload local files (button or drag & drop), download remote files to disk, and delete.
- Deployment tracking — every attempt is recorded with success / failure / reverted status, server, service, jar name, target path and timestamp. Home shows totals; the history screen lists them all.
Requirements on the server
- SSH access with the configured username / password.
systemdfor services (or leave the unit name blank to just swap the jar).- If commands need root (
systemctl, writing to/opt, …), keep Use sudo on — the password is fed tosudo -S.
Only the jar at each service's configured path is backed up/replaced. Other
.jarfiles in the directory are left alone.
Run it
flutter pub get
flutter run -d windows # or macos / linux / <android-device-id>
Build
flutter build windows # macos | linux | apk | ios
Android setup note
Building the Android APK needs the Android SDK 36 and accepted licenses:
flutter doctor --android-licenses
# install SDK 36 via Android Studio > SDK Manager, then:
flutter build apk
minSdk is 24 (required by secure storage). Linux desktop builds must be done
on a Linux host.