July 21

Syncerman: от bash-скриптов к Go cli для пяти облаков одновременно

Иногда, чтобы решить свою проблему, приходится написать инструмент. А иногда — чтобы написать инструмент, приходится решить свою проблему. Короче, как обычно, всё началось не с идеи сделать утилиту, а с того, что добавилась пара важных документов (по иппотке) к существующему зоопарку данных, которые надо хранить.

Как я к этому пришел

У меня давно назрела проблема: синхронизировать разные файлы с разных рабочих мест — по крайней мере дома и на работе. Документы (важные), сохранения из игр, книги для читалки. Ну и надёжный архив всего этого добра, потому что терять даже что-то незначительное это боль 🤕.

Просто положить всё в одно облако — хорошо, но нужен ещё постоянный процесс актуализации. Яндекс Диск работает, Google Drive может изменить условия, Dropbox может внезапно ограничить доступ. Поэтому идея была простая: хранить копии везде. Яндекс Диск, Google Drive, Mail.ru Cloud, Dropbox — чем больше, тем надёжнее. Только файлы, только хардкор!


Раньше я обходился консольным демоном yandex-disk — он работал, но только с Яндексом. Потом перешёл на самописные bash-скрипты, которые вызывали rclone напрямую. Это работало… до поры до времени. Конфигурация разрасталась, порядок синхронизации был важен (нельзя синкать из пустого облака в другое до того, как туда прилетели файлы) и всякое подобное. Проблема!

Когда появились нормальные ИИ-ассистенты для кодинга, я решил, что пора пробовать, а мне нужен нормальный инструмент. Declarative configuration, predictable order, automatic error handling и пр. Так родился Syncerman.

Из говна и палок архитектура и технологии

Syncerman написан на Go — выбрал его только за простоту распространения (один бинарник).

В основе лежит rclone bisync — двусторонняя синхронизация с сохранением метаданных. Syncerman не пытается переизобрести колесо синхронизации, а выступает оркестратором над rclone, предоставляя удобный CLI и YAML-конфигурацию. Никаких серверов и БД.

YAML-конфигурация

Вместо папок с ссылками на bash-скрипты — чёткая структура:

jobs:
  important:
    name: "Важное"
    priority: 10
    tasks:
      - from: "local:/home/jerry/cloud/mirror/Documents"
        to:
          - path: "gdrive:folders/Documents"
          - path: "ydisk:folders/Documents"
      - from: local:Наследие
        to:
          - path: gdrive:Наследие/
            args: [ "--force" ]
          - path: ydisk:Наследие/
            args: [ "--force" ]
          - path: mailru:Наследие/
            args: [ "--force" ]

  games:
    name: "игрули"
    priority: 20
    tasks:
      - from: "local:games_sync"
        to:
          - path: "ydisk:folders/games_sync"

Порядок tasks и to внутри массивов сохраняется строго. Это критично для цепочек: local → Google Drive → Yandex Disk. Без этого — катастрофа.

Проблемасики

rclone bisync требует state-файлы для работы (они лежат где-то в папке пользователя). При первом запуске на новой паре путей он падает с ошибкой "cannot find prior Path1 or Path2 listings". Syncerman детектирует это по двум паттернам в выводе и автоматически перезапускает синхронизацию с флагом --resync. Пользователь даже не замечает проблемы. Раньше делал руками.

На ранних этапах обнаружился критический баг: из-за использования map в Go порядок выполнения целей был недетерминированным. Для линейных цепочек это катастрофа: если сначала синкать из пустого Yandex в Google, а потом из локальной папки в Google — быть беде. Проблема! Так появились массивы.

Результат

Syncerman версии **0.3.6** сейчас работает в production — то есть на моих машинах :) Само собой нужны пруфы, и их есть у меня!

  • бинарники для Linux и Windows (сборка сразу подо всё)
  • GitLab CI как в лучших домах
  • Покрытие тестами - самому было бы лень писать а тут юнит-тесты для всех ключевых пакетов (config, rclone, sync), интеграционные тесты (не просто интеграционные а целые bash-сценарии с десятком шагов).

Публичные репозитории (это мой фетиш):

Использование

Мой crontab выглядит так:

# Синхронизация документов и сохранений 2 рааз в час и лог для ручной проверки
10,30 * * * *   /bin/sh -lc "cd /home/jerry/cloud/mirror && syncerman sync 2>&1 | tee ./lastrun.log"

Конфигурация зеркалирует локальные папки во все облака (настроенные в rclone) одновременно, а затем синхронизирует облака между собой. Если одно облако отвалится — данные остаются в остальных. Если rclone выдаст first-run ошибку — Syncerman сам всё починит.

Вместо вывода

Появление ИИ-ассистентов снизило порог входа для написания таких инструментов. Раньше мне было лень писать то, что и так работает с помощью пары строк на bash. А сейчас это делаю не я :) — разбор выхлопов rclone, обработка edge cases и тестирование делаются очень легко. Я могу заняться решением проблем (архитектурой и требованиями), а рутина "делается сама".


Второй фетиш это ссылочки:

  • rclone bisync - это база
  • unison - еще одна классная штука по теме синхронизации, она прочно заняла место в моём сердце, но об это в другой раз