Add Windows one-click zip and Debian timer/tray packaging
Bundles forked rclone + rclone-bisyncd. Does not vendor dist binaries.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
module rclone-bisyncd
|
||||
|
||||
go 1.22
|
||||
@@ -0,0 +1,159 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Path1 string `json:"path1"`
|
||||
Path2 string `json:"path2"`
|
||||
Workdir string `json:"workdir"`
|
||||
IntervalMinutes int `json:"interval_minutes"`
|
||||
Rclone string `json:"rclone"`
|
||||
CheckFilename string `json:"check_filename"`
|
||||
}
|
||||
|
||||
func defaultConfig() Config {
|
||||
return Config{
|
||||
Enabled: false,
|
||||
IntervalMinutes: 15,
|
||||
Rclone: "rclone",
|
||||
CheckFilename: "RCLONE_TEST",
|
||||
}
|
||||
}
|
||||
|
||||
func configPath() string {
|
||||
if p := os.Getenv("RCLONE_BISYNC_CONFIG"); p != "" {
|
||||
return p
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
base := os.Getenv("LOCALAPPDATA")
|
||||
if base == "" {
|
||||
base = os.Getenv("USERPROFILE")
|
||||
}
|
||||
return filepath.Join(base, "rclone-dropbox-bisync", "config.json")
|
||||
}
|
||||
home, _ := os.UserHomeDir()
|
||||
return filepath.Join(home, ".config", "rclone-dropbox-bisync", "config.json")
|
||||
}
|
||||
|
||||
func loadConfig() (Config, error) {
|
||||
cfg := defaultConfig()
|
||||
b, err := os.ReadFile(configPath())
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
if err := json.Unmarshal(b, &cfg); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
if cfg.IntervalMinutes < 5 {
|
||||
cfg.IntervalMinutes = 5
|
||||
}
|
||||
if cfg.Rclone == "" {
|
||||
cfg.Rclone = "rclone"
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func notify(title, body string) {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
_ = exec.Command("notify-send", "-a", "rclone-bisync", title, body).Run()
|
||||
case "windows":
|
||||
ps := fmt.Sprintf(`Add-Type -AssemblyName System.Windows.Forms; $n=New-Object System.Windows.Forms.NotifyIcon; $n.Icon=[System.Drawing.SystemIcons]::Information; $n.Visible=$true; $n.ShowBalloonTip(4000,'%s','%s',[System.Windows.Forms.ToolTipIcon]::Info); Start-Sleep -Seconds 5; $n.Dispose()`,
|
||||
escapePS(title), escapePS(body))
|
||||
_ = exec.Command("powershell", "-NoProfile", "-Command", ps).Run()
|
||||
}
|
||||
}
|
||||
|
||||
func escapePS(s string) string {
|
||||
out := make([]byte, 0, len(s))
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == '\'' {
|
||||
out = append(out, '\'', '\'')
|
||||
} else {
|
||||
out = append(out, s[i])
|
||||
}
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func runOnce(cfg Config) error {
|
||||
if !cfg.Enabled {
|
||||
return fmt.Errorf("disabled in %s", configPath())
|
||||
}
|
||||
if cfg.Path1 == "" || cfg.Path2 == "" || cfg.Workdir == "" {
|
||||
return fmt.Errorf("path1, path2, and workdir must be set in %s", configPath())
|
||||
}
|
||||
args := []string{
|
||||
"bisync", cfg.Path1, cfg.Path2,
|
||||
"--workdir", cfg.Workdir,
|
||||
"--delta-list",
|
||||
"--check-access",
|
||||
"--check-filename", cfg.CheckFilename,
|
||||
"--compare", "size,modtime,checksum",
|
||||
"--slow-hash-sync-only",
|
||||
"--max-delete", "10",
|
||||
"--resilient",
|
||||
"--recover",
|
||||
"--conflict-resolve", "none",
|
||||
}
|
||||
cmd := exec.Command(cfg.Rclone, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func main() {
|
||||
once := false
|
||||
loop := true
|
||||
for _, a := range os.Args[1:] {
|
||||
switch a {
|
||||
case "--once":
|
||||
once = true
|
||||
loop = false
|
||||
case "--help", "-h":
|
||||
fmt.Fprintf(os.Stderr, "rclone-bisyncd [--once]\nconfig: %s\n", configPath())
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
cfg, err := loadConfig()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "config: %v\n", err)
|
||||
notify("rclone-bisync", "No config yet — edit "+configPath())
|
||||
if once {
|
||||
os.Exit(1)
|
||||
}
|
||||
} else if !cfg.Enabled {
|
||||
fmt.Println("disabled; sleeping")
|
||||
} else if err := runOnce(cfg); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "bisync: %v\n", err)
|
||||
notify("rclone-bisync failed", err.Error())
|
||||
if once {
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
notify("rclone-bisync", "Run finished")
|
||||
if once {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !loop {
|
||||
return
|
||||
}
|
||||
mins := 15
|
||||
if cfg, err := loadConfig(); err == nil && cfg.IntervalMinutes > 0 {
|
||||
mins = cfg.IntervalMinutes
|
||||
}
|
||||
time.Sleep(time.Duration(mins) * time.Minute)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user