🔧 Кросс-сборка CLI и GUI-приложения на Rust — проблемы и решения

DotPlus — это Rust-приложение с графическим и консольным интерфейсом. Изначально оно разрабатывалось под Windows, но в планах была сборка под Linux и Docker.

Решение: добавить полноценные Linux-сборки с поддержкой GUI и CLI и обеспечить стабильную доставку под несколько архитектур.

❗ Почему не просто cargo build?

Если хочется стабильную сборку не только под x86_64, но и под ARM (Raspberry Pi, серверы), одной команды cargo build недостаточно. Возникают проблемы:

  • Нужны кросс-компиляторы (gcc-aarch64-linux-gnu и др.)

  • Требуется ручная настройка sysroot, musl, glibc

  • При использовании GUI (через eframe и egui) иногда всплывают платформозависимые проблемы с OpenGL, зависимостями или rfd (файловые диалоги)

🛠 Решение: cross-rs

Чтобы не городить окружения вручную и не писать десятки --target-флагов с настройкой toolchain, я решил использовать cross — это официальный инструмент от сообщества Rust, который:

  • Использует Docker-образы с преднастроенным окружением для каждой архитектуры

  • Упрощает кросс-компиляцию одной командой:

cross build --release --target aarch64-unknown-linux-gnu
  • Работает из коробки как с CLI, так и с GUI (если система сборки внутри контейнера поддерживает нужные dev-пакеты)

⚙️ Конфигурация: cross.toml

Вот пример моего cross.toml, где перечислены поддерживаемые архитектуры:

[target.aarch64-unknown-linux-gnu]
image = "ghcr.io/cross-rs/aarch64-unknown-linux-gnu:edge"

[target.armv7-unknown-linux-gnueabihf]
image = "ghcr.io/cross-rs/armv7-unknown-linux-gnueabihf:edge"

[target.x86_64-unknown-linux-gnu]
image = "ghcr.io/cross-rs/x86_64-unknown-linux-gnu:edge"

Образы edge — это свежие контейнеры с последними версиями компилятора и зависимостей. Именно они обеспечивают корректную сборку egui, eframe и rfd в Linux-окружении.

🧱 GUI + CLI: архитектура фич

[features]
default = ["gui"]
gui = ["eframe", "egui", "rfd", "egui_extras"]
notifications = ["notify-rust"]

Благодаря этому:

  • Сборка CLI-версии возможна без GUI (--no-default-features)

  • На Linux можно собирать оба варианта одновременно, включая полноценное GTK/Wayland GUI через eframe

Такой подход оказался крайне удобным: в одном проекте можно выпускать и headless-бинарник, и полноценное графическое приложение.

🪓 От чего пришлось отказаться

Несмотря на успех с cross, не обошлось без потерь:

  • musl-сборки (статически линкуемые) были отброшены — eframe и rfd плохо работают в таком окружении

  • Windows ARM MIPS и прочие экзотические платформы — слишком нестабильны и слабо поддерживаются в cross

  • Слишком "чистая" сборка без Docker — пришлось отказаться от идеи собирать без контейнеров, так как зависимостей GUI слишком много, и они не всегда есть на GitHub Actions runner'ах

✅ Что в итоге получилось

В результате, с помощью cross теперь собираются полноценные GUI+CLI бинарники под Linux для трёх платформ:

Архитектура

Поддержка

Назначение

x86_64-unknown-linux-gnu

✅ GUI/CLI

Десктопы и серверы

aarch64-unknown-linux-gnu

✅ GUI/CLI

ARM64 серверы, RPi 4 и новее

armv7-unknown-linux-gnueabihf

✅ GUI/CLI

RPi 3 и другие ARMv7 SBC

✅ Итог: благодаря cross

DotPlus теперь полноценно работает на Linux с графическим интерфейсом и CLI. Одинаковый UX, стабильные сборки, возможность использовать на Raspberry Pi и сервере — всё это стало реальностью.

🚀 CI/CD: автоматизация сборки, кросс-компиляции и релизов

После успешной настройки cross для локальной кросс-компиляции стало ясно: ручной процесс не масштабируется. Хотелось воспроизводимой, полностью автоматизированной системы, которая при любом обновлении могла бы:

  • Собрать DotPlus под несколько архитектур

  • Подготовить установочные пакеты .deb и .rpm

  • Собрать и опубликовать Docker-образ

  • Сформировать релиз и прикрепить артефакты

Так родился CI/CD-процесс на базе GitHub Actions, который покрывает весь цикл сборки и доставки.

📂 Общая структура

CI/CD реализован в одном workflow-файле 📄 build-multiarch.yml:

name: Build dotplus (.deb/.rpm) for all architectures

on:
  push:
    branches: [linux]
    tags: ["v*"]
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      CROSS_SKIP_VERSION_CHECK: "1"
    steps:
      - name: 🧾 Checkout
        uses: actions/checkout@v4

      - name: ⚡️ Cache Cargo
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/bin
            ~/.cargo/registry
            ~/.cargo/git
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-

      - name: 🧬 Enable QEMU for multi-arch RPM
        uses: docker/setup-qemu-action@v3
        with:
          platforms: all

      - name: 🦀 Install Rust
        uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          override: true
          components: rustfmt

      - name: 🎯 Add targets
        run: |
          rustup target add \
            x86_64-unknown-linux-gnu \
            aarch64-unknown-linux-gnu \
            armv7-unknown-linux-gnueabihf

      - name: 🐳 Install Docker & Cross
        run: |
          sudo apt update
          sudo apt install -y docker.io containerd
          if ! command -v cross &>/dev/null; then
            cargo install cross
          else
            echo "✅ cross уже установлен, пропускаем"
          fi

      - name: 📦 Install deb/rpm tools
        run: |
          sudo apt install -y \
            dpkg-dev debhelper rpm rpm2cpio fakeroot \
            qemu-user-static binfmt-support \
            gcc-multilib libc6-dev-i386 pkg-config \
            libssl-dev libstdc++-12-dev

      - name: 🔧 Make script executable
        run: chmod +x tools/generate.sh

      - name: 🛠 Run build script
        run: ./tools/generate.sh

      - name: ☁️ Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: dotplus-packages
          path: |
            build/**/dotplus_*.deb
            build/**/RPMS/**/*.rpm

  docker:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: 🧾 Checkout
        uses: actions/checkout@v4

      - name: 🦀 Install Rust + cross
        run: |
          rustup update
          rustup target add x86_64-unknown-linux-musl
          cargo install cross

      - name: 🛠 Build static binary (musl, CLI-only)
        run: cross build --release --target x86_64-unknown-linux-musl --no-default-features

      - name: 🧰 Prepare Docker context
        run: |
          if [[ -f target/x86_64-unknown-linux-musl/release/dotplus ]]; then
            cp target/x86_64-unknown-linux-musl/release/dotplus docker/dotplus
          else
            echo "❌ Static binary not found"
            exit 1
          fi

      - name: 🐳 Build Docker image
        run: docker build -t dotplus-cli -f docker/Dockerfile.cli ./docker

      - name: ☁️ Save Docker image as archive
        run: docker save dotplus-cli | gzip > dotplus-cli.tar.gz

      - name: ☁️ Upload Docker image artifact
        uses: actions/upload-artifact@v4
        with:
          name: dotplus-cli-docker
          path: dotplus-cli.tar.gz

📄 build-multiarch.yml запускается при пуше в ветки release-linux или release-docker и выполняет сборку, упаковку, публикацию и деплой.

⚙️ Этапы пайплайна

Дата: 01.07.2025
Файл Workflow: build-multiarch.yml
Триггер: Push в ветку linux

📦 Этап 1: build (⏱ ~16 минут)

Шаг

Описание

⚙️ Set up job

Инициализация задания

📥 Checkout

Клонирование репозитория

Cache Cargo

Кэширование зависимостей

🧩 Enable QEMU for multi-arch RPM

Эмуляция архитектур

🦀 Install Rust

Установка Rust

🎯 Add targets

Добавление целевых архитектур

🐳 Install Docker & Cross

Установка Docker и cross

🧰 Install deb/rpm tools

Установка сборочных инструментов

🔧 Make script executable

Подготовка сборочного скрипта

▶️ Run build script

Сборка .deb и .rpm

⬆️ Upload artifacts

Загрузка артефактов

♻️ Post-steps

Очистка: откат QEMU, кэша и checkout

📤 Артефакты: .deb, .rpm
📤 Артефакты: .deb, .rpm

🐳 Этап 2: docker (⏱ ~4 минуты)

Шаг

Описание

⚙️ Set up job

Инициализация задания

📥 Checkout

Клонирование репозитория

🦀 Install Rust + cross

Установка Rust + кросс-компиляция

🛠 Build static binary

Сборка статичного бинарника (musl, CLI-only)

🧳 Prepare Docker context

Подготовка контекста для Docker

🔨 Build Docker image

Сборка образа

💾 Save Docker image

Сохранение Docker-образа в архив

⬆️ Upload Docker artifact

Загрузка архива как артефакт

♻️ Post-steps

Очистка: откат checkout

📤 Артефакт: архив Docker-образа
📤 Артефакт: архив Docker-образа
🔄 Общая структура пайплайна
🔄 Общая структура пайплайна

🔄 Кросс-сборка бинарников

Кросс-компиляция происходит с использованием cross, который изолирует сборку в готовых Docker-образах:

- name: Build for aarch64
  run: cross build --release --target aarch64-unknown-linux-gnu

- name: Build for armv7
  run: cross build --release --target armv7-unknown-linux-gnueabihf

- name: Build for x86_64
  run: cross build --release --target x86_64-unknown-linux-gnu

Это гарантирует одинаковые бинарники независимо от среды, включая GUI-часть на Linux.

📦 Пакетирование: .deb и .rpm из CI

  • dotplus_1.0.0_amd64.deb

  • dotplus_1.0.0_arm64.deb

  • dotplus_1.0.0_armhf.deb

  • dotplus-1.0.0-1.x86_64.rpm

Установка на стороне пользователя:

sudo dpkg -i dotplus_1.0.0_amd64.deb
# или
sudo rpm -i dotplus-1.0.0-1.x86_64.rpm

🔧 Как это устроено?

Проект включает шаблоны:

templates/control.in — шаблон для .deb

Package: dotplus
Version: {{VERSION}}
Section: utils
Priority: optional
Architecture: {{ARCH}}
Maintainer: Danil Nigmatullin <danil.communication@gmail.com>
Depends: {{DEPENDS}}
Description: QR and barcode generator with logo and CSV support (Rust CLI/GUI)
 DotPlus is a Rust-based GUI and command-line tool for generating QR codes and barcodes.
 It supports logo embedding, CSV import, and auto-update functionality.
 Suitable for both terminal workflows and desktop users.

templates/spec.in — шаблон для .rpm

Name:           dotplus
Version:        {{VERSION}}
Release:        1%{?dist}
Summary:        QR and barcode generator with logo and CSV support (Rust CLI/GUI)

License:        Freeware (Proprietary)
URL:            https://github.com/nigdanil/dotplus

Source0:        dotplus
BuildArch:      {{ARCH}}
Requires:       {{REQUIRES}}

%description
DotPlus is a Rust-based GUI and CLI tool for QR/barcode generation with logo and CSV support.

%prep
# No preparation steps needed

%install
[ -f %{SOURCE0} ] || { echo "Missing binary dotplus in SOURCES"; exit 1; }
[ -f %{_sourcedir}/arialmt.ttf ] || { echo "Missing font file arialmt.ttf"; exit 1; }

mkdir -p %{buildroot}/usr/bin
mkdir -p %{buildroot}/usr/share/dotplus/fonts

install -m 0755 %{SOURCE0} %{buildroot}/usr/bin/dotplus
install -m 0644 %{_sourcedir}/arialmt.ttf %{buildroot}/usr/share/dotplus/fonts/arialmt.ttf

%files
/usr/bin/dotplus
/usr/share/dotplus/fonts/arialmt.ttf

%changelog
* Mon Jun 30 2025 Danil Nigmatullin <danil.communication@gmail.com> - {{VERSION}}-1
- Initial RPM build

А также вспомогательный скрипт 📄 tools/generate.sh:

    # tools/generate.sh
    #!/usr/bin/env bash
    set -e

    VERSION="1.0.0"
    echo "🚀 Starting multi-arch build: version $VERSION"
    START=$(date +%s)

    ARCHS=(
      "amd64"    # x86_64-unknown-linux-gnu
      "arm64"    # aarch64-unknown-linux-gnu
      "armhf"    # armv7-unknown-linux-gnueabihf
    )
    TARGETS=(
      "x86_64-unknown-linux-gnu"
      "aarch64-unknown-linux-gnu"
      "armv7-unknown-linux-gnueabihf"
    )
    DEB_ARCHS=(
      "amd64"
      "arm64"
      "armhf"
    )
    RPM_ARCHS=(
      "x86_64"
      "aarch64"
      "armv7hl"
    )

    ROOT_DIR="$(git rev-parse --show-toplevel)"

    TEMPLATE_DIR="$ROOT_DIR/templates"

    mkdir -p build

    FAILED=()

    for i in "${!ARCHS[@]}"; do
      ARCH="${ARCHS[$i]}"
      TARGET="${TARGETS[$i]}"
      DEB_ARCH="${DEB_ARCHS[$i]}"
      RPM_ARCH="${RPM_ARCHS[$i]}"

      echo "🔧 Building for $ARCH ($TARGET)..."

      CROSS_SKIP_VERSION_CHECK=1 cross build \
        --release \
        --target "$TARGET" \
        --manifest-path Cargo.toml \
        --verbose || { echo "⚠️ Build failed for $TARGET"; FAILED+=("$ARCH"); continue; }

      BIN="target/$TARGET/release/dotplus"

      ls -lh "target/$TARGET/release"

      if [[ ! -f "$BIN" ]]; then
        echo "❌ Build failed for $TARGET"
        FAILED+=("$ARCH")
        continue
      fi

      BUILD_DIR="build/$ARCH"
      DEB_DIR="$BUILD_DIR/deb"
      RPM_DIR="$BUILD_DIR/rpm"

      mkdir -p "$DEB_DIR/DEBIAN"
      mkdir -p "$DEB_DIR/usr/bin"
      mkdir -p "$RPM_DIR/SOURCES"

      cp "$BIN" "$DEB_DIR/usr/bin/dotplus"
      cp "$BIN" "$RPM_DIR/SOURCES/dotplus"
      cp src/tools/font/arialmt.ttf "$RPM_DIR/SOURCES/arialmt.ttf"

      # Копируем ресурсные файлы (шрифты)
      mkdir -p "$DEB_DIR/usr/share/dotplus/fonts"
      cp src/tools/font/arialmt.ttf "$DEB_DIR/usr/share/dotplus/fonts/"

      # === .deb ===
      sed \
        -e "s/{{VERSION}}/$VERSION/" \
        -e "s/{{ARCH}}/$DEB_ARCH/" \
        -e "s/{{DEPENDS}}/libc6 (>= 2.29)/" \
        "$TEMPLATE_DIR/control.in" > "$DEB_DIR/DEBIAN/control"

      dpkg-deb --build "$DEB_DIR" "$BUILD_DIR/dotplus_${VERSION}_${DEB_ARCH}.deb"

      # === .rpm ===
      SPEC_OUT="$RPM_DIR/dotplus.spec"
      sed \
        -e "s/{{VERSION}}/$VERSION/" \
        -e "s/{{ARCH}}/$RPM_ARCH/" \
        -e "s/{{REQUIRES}}/glibc >= 2.29/" \
        "$TEMPLATE_DIR/spec.in" > "$SPEC_OUT"

      RPM_BUILDROOT="$RPM_DIR/BUILDROOT/dotplus-${VERSION}-1.${RPM_ARCH}"

      mkdir -p "$RPM_BUILDROOT/usr/bin"
      cp "$BIN" "$RPM_BUILDROOT/usr/bin/dotplus"
      chmod +x "$RPM_BUILDROOT/usr/bin/dotplus"

      # Копируем ресурсные файлы (шрифты)
      FONT_SOURCE="src/tools/font/arialmt.ttf"
      FONT_DEST="$RPM_DIR/BUILDROOT/dotplus-${VERSION}-1.${RPM_ARCH}/usr/share/dotplus/fonts"
      mkdir -p "$FONT_DEST"

      if [[ -f "$FONT_SOURCE" ]]; then
        cp "$FONT_SOURCE" "$FONT_DEST"
        echo "✅ Шрифт скопирован: $FONT_SOURCE → $FONT_DEST"
      else
        echo "⚠️ WARNING: $FONT_SOURCE не найден — удаляем строку из .spec"
        sed -i '/\/usr\/share\/dotplus\/fonts\/arialmt.ttf/d' "$SPEC_OUT"
      fi

      if [[ "$ARCH" == "amd64" ]]; then
        echo "📦 Building RPM for $ARCH..."
        rpmbuild \
          --define "_topdir $ROOT_DIR/$RPM_DIR" \
          --define "_sourcedir $ROOT_DIR/$RPM_DIR/SOURCES" \
          --define "_builddir $ROOT_DIR/$RPM_DIR/BUILD" \
          --define "_specdir $ROOT_DIR/$RPM_DIR" \
          --define "_rpmdir $ROOT_DIR/$RPM_DIR/RPMS" \
          --define "_buildrootdir $ROOT_DIR/$RPM_DIR/BUILDROOT" \
          --target "$RPM_ARCH-redhat-linux" \
          -bb "$SPEC_OUT" || {
            echo "❌ RPM build failed for $TARGET"
            FAILED+=("$ARCH")
            continue
          }

        RPM_OUTPUT=$(find "$RPM_DIR/RPMS" -name "*.rpm")
        if [[ -n "$RPM_OUTPUT" ]]; then
          echo "✅ RPM built for $ARCH: $RPM_OUTPUT"
        else
          echo "❌ RPM not found for $ARCH"
          FAILED+=("$ARCH")
        fi
      else
        echo "⚠️ Skipping RPM build for $ARCH"
      fi

      echo "✅ RPM build completed for $ARCH"
      
      echo "📁 RPM output dir listing:"
      ls -lR "$RPM_DIR"

      echo "📦 Checking built RPMs:"
      find "$RPM_DIR/RPMS" -type f 2>/dev/null || true

    done

    END=$(date +%s)
    DURATION=$((END - START))

    if [[ ${#FAILED[@]} -gt 0 ]]; then
      echo "❌ Failed builds: ${FAILED[*]}"
    else
      echo "✅ All target builds succeeded"
    fi

    echo "🎉 Build finished in $DURATION seconds."

    # 🧪 Покажем, какие пакеты были созданы
    echo "📦 Built package files:"
    find build -type f \( -name "*.deb" -o -name "*.rpm" \)

    # Подсчёт количества .deb и .rpm
    PACKAGE_COUNT=$(find build -type f \( -name "*.deb" -o -name "*.rpm" \) | wc -l | tr -d '[:space:]')
    
    echo "🧪 DEBUG: Found packages:"
    FOUND_PACKAGES=$(find build -type f \( -name "*.deb" -o -name "*.rpm" \))
    echo "$FOUND_PACKAGES"

    # Если ни одного пакета не создано — ошибка
    if [[ "$PACKAGE_COUNT" -lt 1 ]]; then
      echo "❌ No packages were created (check target paths or build errors)"
      echo "💡 Hint: verify that .deb and .rpm were written to expected build/**/ paths"
      exit 1
    fi

    echo "✅ Total packages created: $PACKAGE_COUNT"

Он принимает бинарник и архитектуру, генерирует .deb/.rpm с помощью dpkg-deb и rpmbuild, и сохраняет итоговые пакеты для релиза.

🐳 Docker-сборка

После сборки бинарников запускается Docker-сборка CLI-варианта DotPlus:

- name: Build Docker image
  run: |
    docker build -f docker/Dockerfile.cli -t ghcr.io/nigdanil/dotplus:latest .
    docker push ghcr.io/nigdanil/dotplus:latest

Простой Dockerfile.cli использует минимальную базу и добавляет только бинарник:

FROM debian:bookworm-slim
COPY dotplus /usr/local/bin/dotplus
ENTRYPOINT ["dotplus"]

Публикация артефактов

Все собранные файлы (бинарники, .deb, .rpm) автоматически прикрепляются к GitHub Release. Это упрощает дистрибуцию: пользователи могут выбрать нужную архитектуру и просто скачать готовый пакет.

🧩 Что пришлось уче��ть

  • musl и arm64-darwin не поддерживаются — нет стабильных образов или зависимости несовместимы

  • ⚠️ cross не кеширует образы между пушами, поэтому Docker нужен как для сборки, так и для доставки

📌 Итоговая картина релиза

Формат

Архитектуры

Описание

.tar.gz (бинарник)

x86_64, armhf, arm64

Универсальная ручная установка

.deb

x86_64, armhf, arm64

Ubuntu, Debian, Raspberry Pi

.rpm

x86_64

Fedora, RHEL, CentOS, openSUSE

Docker

Любая (универсально)

Без установки, CI/CD, DevOps

🐳 Docker: зачем он понадобился и как встроился в сборку

Изначально идея добавить Docker-образ DotPlus не стояла на первом месте. Основной упор делался на нативные бинарники и установочные пакеты. Однако в процессе развития проекта стало ясно: Docker — это не просто "в довесок", а необходимый инструмент, без которого:

  • невозможно обеспечить стабильную кросс-сборку в CI;

  • нельзя удобно использовать CLI в автоматических сценариях;

  • сложно дистрибутировать DotPlus в окружениях DevOps и серверов.

🔍 Зачем вообще нужен Docker?

Основные причины интеграции Docker:

  1. Надежная изоляция окружения в CI/CD
    Даже cross работает внутри Docker-контейнеров. Если ты не контролируешь окружение сборки (например, на GitHub Actions), Docker становится стандартом.

  2. Упрощённый запуск без установки
    CLI-версия DotPlus можно использовать прямо из Docker без установки бинарников, зависимостей и пакетов. Это особенно ценно для DevOps и автоматизации.

  3. Платформа-агностичный дистрибутив
    Один образ — один результат. Его поведение одинаково в любой системе, будь то Windows, Linux или macOS (с установленным Docker).

  4. Разделение GUI и CLI
    Docker используется только для CLI. Это упрощает образ, делает его минимальным и не тянет GUI-библиотеки.

🛠 Структура Dockerfile

В проекте используется Dockerfile.cli:

FROM debian:bookworm-slim
COPY dotplus /usr/local/bin/dotplus
ENTRYPOINT ["dotplus"]

Это минимальный образ (на базе slim-дистрибутива Debian), в который помещается только собранный бинарник dotplus. Никаких дополнительных зависимостей, библиотек или окружения не требуется.

⚙️ Интеграция в CI

Сборка Docker-образа происходит автоматически, после компиляции CLI:

- name: Build Docker image
  run: |
    docker build -f docker/Dockerfile.cli -t ghcr.io/nigdanil/dotplus:latest .
    docker push ghcr.io/nigdanil/dotplus:latest

Docker-образ публикуется в Docker Hub, откуда его может скачать любой пользователь:

docker pull nigdanil/dotplus-cli:latest

🚀 Как использовать Docker-версию DotPlus

Пример запуска CLI внутри Docker:

docker run --rm -v $(pwd):/out ghcr.io/nigdanil/dotplus:latest \
  --text "Docker test" --format qrcode --output /out/code.png

Объяснение:

  • --rm — удаляет контейнер после завершения

  • -v $(pwd):/out — монтирует текущую папку внутрь контейнера для вывода результата

  • --text, --format, --output — аргументы CLI DotPlus

Таким образом, можно использовать DotPlus в любом CI, на сервере, на macOS или даже в WSL — не устанавливая ничего, кроме Docker.

💻 Запуск DotPlus через Docker на Windows (.bat-файлы)

👉 Для пользователей Windows подготовлены удобные .bat-скрипты, которые вызывают CLI DotPlus внутри Docker-контейнера. Это позволяет запускать генерацию QR и штрихкодов без установки зависимостей.

📄 run-qr.bat — генерация QR-кодов

@echo off
REM Генерация QR-кодов из CSV через Docker

docker run --rm -v "%cd%\examples:/examples" nigdanil/dotplus-cli:1.0.0 ^
  --mode qr ^
  --csv "/examples/magnit/data/qr/magnit.csv" ^
  --logo "/examples/magnit/logo/v1.png" ^
  --output "/examples/magnit/img/qr" ^
  --cols 3 ^
  --rows 3 ^
  --qr-size 150 ^
  --font-size 24 ^
  --font-color "#000000"

pause

📄 run-barcode.bat — генерация EAN-13 штрихкодов

@echo off
REM Генерация штрихкодов EAN-13 из CSV через Docker

docker run --rm -v "%cd%\examples:/examples" nigdanil/dotplus-cli:1.0.0 ^
  --mode barcode ^
  --csv "/examples/magnit/data/barcode/magnit-barcodes-EAN-13.csv" ^
  --output "/examples/magnit/img/barcode" ^
  --barcode-type EAN-13 ^
  --cols 3 ^
  --rows 4 ^
  --width 300 ^
  --height 100 ^
  --font-size 22 ^
  --label-height 40 ^
  --offset-y 10 ^
  --spacing-x 20 ^
  --spacing-y 20

pause

📂 Структура проекта для скриптов

D:\dotplus-windows\
  ├── examples\
  ├── run-qr.bat
  └── run-barcode.bat

📌 Скрипты монтируют папку examples внутрь контейнера и сохраняют результат в удобное место. Всё, что нужно — это установленный Docker.

✅ Почему это удобно?

  • Не нужно устанавливать Rust, зависимости или сам dotplus

  • Работает на любой машине с Docker

  • Можно интегрировать в автоматизацию, скрипты, CI/CD

📌 Что удалось благодаря Docker

Проблема

Решение через Docker

Кросс-сборка в CI

cross + Docker-образы

Использование без установки

Образ с CLI доступен публично

Интеграция в shell-скрипты и пайплайны

CLI можно вызывать из любого скрипта

Минимизация зависимости

Образ содержит только dotplus бинарник

Вывод: Docker стал неотъемлемой частью инфраструктуры DotPlus. Он используется и в процессе сборки, и как самостоятельный способ запуска CLI. Это позволяет дистрибутировать инструмент в любую среду — будь то сервер, CI/CD или чужой компьютер без установки зависимостей.

📜 Лицензия DotPlus: от исходников к закрытому дистрибутиву

В первой версии DotPlus проект поставлялся с открытым кодом. Однако с релизом стабильных версий и запуском сборок под Linux, стало ясно: пора пересмотреть подход к лицензированию. Причин этому — несколько, и они связаны как с техническими, так и с юридическими и практическими аспектами.

В результате было принято решение:

📌 Перейти на бесплатную, но проприетарную лицензию.

❓ Почему так?

Вот ключевые причины:

  1. Минимизация рисков форков и переиспользования бренда
    Когда проект становится узнаваемым, велик риск "серых" клонов, которые мог��т нарушать философию продукта или использовать имя DotPlus без разрешения.

  2. Снижение нагрузки на поддержку
    Когда код открыт, пользователи часто создают собственные сборки, вносят изменения и потом задают вопросы, не относящиеся к официальной версии.

  3. Защита бизнес-модели и эксклюзивных алгоритмов
    DotPlus использует внутренние техники вставки логотипов, оптимизации QR, компактного вывода и т. д. Их защита — ключ к качеству конечного продукта.

  4. Фокус на стабильных бинарниках, а не на доработках со стороны сообщества
    Вся сборка и дистрибуция централизованы — это позволяет гарантировать качество и одинаковое поведение на всех системах.

✅ Что разрешено по новой лицензии?

  • 🔓 Бесплатное использование для любых целей — как в личных, так и в коммерческих проектах

  • 📦 Распространение неизменённых бинарников

  • 👥 Использование в организациях, автоматизации, CI/CD и т. п.

❌ Что запрещено?

  • 🚫 Изучение, анализ, декомпиляция и модификация бинарников

  • 🚫 Распространение модифицированных версий

  • 🚫 Попытки восстановить или раскрыть исходный код

📁 Как распространяется DotPlus сейчас?

  • ✅ Только в виде бинарников (.exe, .deb, .rpm, .tar.gz)

  • ✅ Через официальный GitHub Releases и Docker Registry

📄 Лицензионное соглашение (фрагмент)

Тип лицензии: Бесплатное ПО (проприетарное)
Исходный код: не распространяется
Использование: разрешено для личного и коммерческого применения
Обратная разработка: строго запрещена
Распространение: допустимо только в виде неизменённых бинарников с включением лицензионного соглашения
Авторские права: исходный код и все права на него остаются за автором

⚠️ Почему это всё равно честно?

Несмотря на закрытую лицензию, DotPlus остаётся:

  • полностью бесплатным;

  • стабильным и регулярно обновляемым;

  • совместимым с Linux, Windows, Docker;

  • понятным и удобным инструментом, который не требует онлайн-доступа или регистрации.

Более того, пользователь всё ещё получает CLI-инструмент, GUI-приложение и все опции (включая CSV, логотипы, экспорт) без ограничений функциональности.

Вывод: лицензия DotPlus теперь закрытая, но прозрачная. Программа остаётся бесплатной, распространяется в виде готовых бинарников и полностью пригодна для реального применения — без страха перед юридическими подводными камнями.

📈 Итоги и планы развития DotPlus

DotPlus прошёл большой путь от простой Windows-программы до полноценного кроссплатформенного инструмента с поддержкой CLI, GUI, Docker и релизами под Linux. Этот этап развития был посвящён тому, чтобы сделать проект устойчивым, универсальным и легко доставляемым в любой среде.

✅ Что реализовано

Возможность

Статус

Комментарий

🖥 GUI для Windows и Linux

✅ Готово

На базе eframe/egui, раб��тает офлайн

🧾 CLI-интерфейс

✅ Готово

Генерация QR/штрихкодов через параметры

📦 DEB и RPM-пакеты

✅ В релизах

Для всех популярных дистрибутивов

🐳 Docker-образ

✅ Опубликован

Поддержка запуска без установки

🔄 CI/CD-сборка и релизы

✅ Автоматизировано

Публикация в GitHub Releases + GHCR

🔐 Проприетарная лицензия

✅ Введена

Бесплатно, без исходников, с разрешением на использование

🛠 Технические особенности

  • Используется cross для мультиархитектурной сборки (x86_64, ARMv7, AArch64)

  • Автоматическая генерация .deb и .rpm через скрипт tools/generate.sh

  • CI/CD на GitHub Actions, один YAML управляет всем пайплайном

  • Распространение Docker-образа через Docker Hub:

docker pull nigdanil/dotplus-cli:latest

🔮 Что в планах?

Вот направления, над которыми планируется работать в следующих релизах:

План

Статус

Комментарий

📄 Экспорт в PDF и SVG

🔜 В процессе

Для векторной печати и типографий

🌐 Web-версия на WASM

🔜 Исследуется

Возможность запуска в браузере без сервера

📄 Поддержка больших CSV

🔜 Планируется

Пакетная генерация с логами и ошибками

🔄 Автообновление для GUI

🔜 Будет

Особенно для Windows-версии

⚙️ Расширение CLI

🔜 Постепенно

Больше форматов, управление шаблонами

🔔 Интеграция уведомлений

🔜 Через notify-rust

Пока опциональная фича

🤝 Обратная связь и участие

Если вам не хватает какой-то функции, формат не поддерживается, или вы хотите предложить идею — открывайте issue на GitHub:

📌 https://github.com/nigdanil/dotplus

✅ Финальный вывод

DotPlus стал стабильным, мощным и универсальным инструментом для генерации QR и штрихкодов.
Поддержка разных архитектур, нативные пакеты, Docker и офлайн-режим делают его отличным выбором для всех — от домашних пользователей до CI-инфраструктур.

Программа продолжит развиваться. Спасибо всем, кто использует, тестирует и даёт обратную связь.