diff --git a/.gitignore b/.gitignore
index b76daa0..b7fd586 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
/public/
+.venv/
.DS_Store
Thumbs.db
diff --git a/DEPLOY_INSTRUCTIONS.md b/DEPLOY_INSTRUCTIONS.md
index 27095e1..c2c5cb3 100644
--- a/DEPLOY_INSTRUCTIONS.md
+++ b/DEPLOY_INSTRUCTIONS.md
@@ -2,29 +2,36 @@
## Описание
-Этот документ описывает процесс публикации сгенерированного сайта (папка `public`) на хостинге через SFTP. Решение кроссплатформенное и работает как на Unix-подобных системах (Linux/macOS), так и на Windows.
+Этот документ описывает процесс публикации сгенерированного сайта (папка `public`)
+на хостинге через SFTP. Решение кроссплатформенное и работает как на Unix-подобных
+системах (Linux/macOS), так и на Windows.
+
+> **ВАЖНО: деплой сайта выполняется ТОЛЬКО через `make deploy`.** Аккаунт Jino —
+> SFTP-only: удалённый запуск команд (rsync, ssh-exec) запрещён сервером
+> (ошибка `exec request failed on channel 0`). Прямой rsync на этот аккаунт
+> НЕ РАБОТАЕТ.
## Учетные данные и параметры подключения
-Для работы используются следующие параметры:
-
- **Хост**: kpa39l.myjino.ru
- **Порт SFTP**: 2222
- **Пользователь**: kpa39l_dedinit
- **Пароль**: -Zp4ep747t25
-- **Целевой путь**: /dedinit.ru (относительно домашнего каталога пользователя)
+- **Целевой путь**: /dedinit.ru (относительно корня аккаунта)
## Установка зависимостей
### Для Linux/macOS
-- **rsync** - установите через менеджер пакетов (например, apt-get install rsync или brew install rsync)
-- **Make** - для выполнения команд из Makefile
+- **Python 3** (с venv/pip)
+- **Make** — для выполнения команд из Makefile
+- Пакет `paramiko` ставится автоматически в `.venv/` при первом `make deploy`
+ (таргет `venv`), либо вручную: `pip install paramiko`
### Для Windows
-- **WinSCP** - установите с официального сайта ()
-- **Make** - можно установить через Chocolatey, MinGW или Git for Windows
+- **WinSCP** — установите с официального сайта ()
+- **Make** — можно установить через Chocolatey, MinGW или Git for Windows
## Использование Makefile
@@ -39,23 +46,28 @@ make build
### Деплой сайта
```bash
+# один раз экспортируем пароль SFTP (каждый раз в новой сессии)
+export SSHPASS='-Zp4ep747t25'
make deploy
```
-Команда сначала собирает сайт, затем загружает содержимое папки `public/` на удаленный сервер.
+ЕДИНСТВЕННЫЙ способ публикации. Сначала собирает сайт, затем зеркалирует
+`public/` на сервер (`/dedinit.ru`) с удалением устаревших файлов
+(аналог `rsync --delete`):
-Для Windows используется WinSCP.com для SFTP-подключения, для Unix-подобных систем - rsync.
+- **Unix**: `tools/deploy_sftp.py` (paramiko, SFTP + рекурсивный mirror)
+- **Windows**: WinSCP.com (скрипт `winscp_script.txt`)
+
+Если `SSHPASS` не задана — `make deploy` завершится с ошибкой и подсказкой.
### Симуляция деплоя
```bash
+export SSHPASS='-Zp4ep747t25'
make deploy-dry-run
```
-Команда сначала собирает сайт, затем симулирует загрузку на сервер:
-
-- На Unix-подобных системах используется флаг `-n` для симуляции rsync
-- На Windows симуляция пока не реализована, будет выведено соответствующее сообщение
+Собирает сайт и показывает, какие файлы будут загружены/удалены, без заливки.
### Очистка
@@ -83,18 +95,15 @@ make help
## Конфигурация
-Параметры подключения находятся в файле Makefile:
+Параметры подключения для Unix находятся в `tools/deploy_sftp.py`
+(переменные `DEDINIT_HOST`, `DEDINIT_PORT`, `DEDINIT_USER`, `DEDINIT_REMOTE`,
+по умолчанию — kpa39l.myjino.ru:2222 / kpa39l_dedinit / /dedinit.ru).
+На Windows — в `Makefile` и `winscp_script.txt`.
-- HOST - адрес сервера
-- PORT - порт SFTP
-- USERNAME - имя пользователя
-- REMOTE_PATH - путь на удаленном сервере (относительно домашнего каталога пользователя)
-
-При необходимости вы можете изменить эти параметры в файле Makefile.
+При необходимости вы можете изменить эти параметры.
## Настройка для Windows
-Для корректной работы на Windows необходимо:
-
-1. Убедиться, что WinSCP установлен в стандартную директорию `C:\Program Files (x86)\WinSCP\`
-2. Файл `winscp_script.txt` содержит команды для подключения и синхронизации файлов
+1. Убедиться, что WinSCP установлен в стандартную директорию
+ `C:\Program Files (x86)\WinSCP\`
+2. Файл `winscp_script.txt` содержит команды для подключения и синхронизации файлов
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 40c1ae6..2e2ac69 100644
--- a/Makefile
+++ b/Makefile
@@ -7,35 +7,48 @@ ifeq ($(OS),Windows_NT)
DRYRUN_CMD = echo "Windows: проверка SFTP-соединения..."
else
PLATFORM = Unix
- # На Unix-подобных системах используем rsync для SFTP
- HOST = kpa39l.myjino.ru
- PORT = 2222
- USERNAME = kpa39l_dedinit
- REMOTE_PATH = /dedinit.ru
+ # На Unix используем paramiko (SFTP). Аккаунт Jino — SFTP-only: rsync/ssh-exec
+ # запрещены ("exec request failed on channel 0"), поэтому deploy через
+ # tools/deploy_sftp.py (рекурсивный mirror + удаление лишнего).
+ # Венв .venv создаётся автоматически таргетом venv (нужен paramiko).
+ DEPLOY_CMD = .venv/bin/python tools/deploy_sftp.py
+ DRYRUN_CMD = .venv/bin/python tools/deploy_sftp.py --dry-run
+ # Пароль берётся из переменной окружения SSHPASS (см. README.md / DEPLOY_INSTRUCTIONS.md)
endif
-.PHONY: build deploy deploy-dry-run clean rebuild help
+.PHONY: build deploy deploy-dry-run clean rebuild help venv
build:
hugo --minify
@echo "Сайт успешно собран в папку public/"
-deploy: build
+# ЕДИНСТВЕННЫЙ способ деплоя — через make deploy.
+# Никогда не заменять на rsync/ssh-exec напрямую: на Jino аккаунт SFTP-only.
+deploy: build venv
ifeq ($(PLATFORM),Windows)
$(DEPLOY_CMD)
else
- rsync -avz --delete -e 'ssh -p $(PORT)' public/ $(USERNAME)@$(HOST):$(REMOTE_PATH)/
+ @test -n "$$SSHPASS" || (echo "ОШИБКА: переменная SSHPASS (пароль SFTP) не задана. См. README.md / DEPLOY_INSTRUCTIONS.md"; exit 1)
+ $(DEPLOY_CMD)
endif
-deploy-dry-run: build
+deploy-dry-run: build venv
ifeq ($(PLATFORM),Windows)
@echo "Выполняем проверку соединения с сервером..."
$(DRYRUN_CMD)
else
- rsync -avn --delete -e 'ssh -p $(PORT)' public/ $(USERNAME)@$(HOST):$(REMOTE_PATH)/
+ @test -n "$$SSHPASS" || (echo "ОШИБКА: переменная SSHPASS (пароль SFTP) не задана. См. README.md / DEPLOY_INSTRUCTIONS.md"; exit 1)
+ $(DRYRUN_CMD)
endif
+
+# Создаёт venv с paramiko для Unix-деплоя (единожды)
+venv:
+ @if [ ! -x .venv/bin/python ]; then \
+ echo "Создаю .venv и ставлю paramiko..."; \
+ python3 -m venv .venv && .venv/bin/pip install -q paramiko; \
+ fi
clean:
ifeq ($(OS),Windows_NT)
- del /q /s public\
+ del /q /s public\/
else
rm -rf public/
endif
@@ -46,8 +59,11 @@ rebuild: clean deploy
help:
@echo "Доступные команды:"
@echo " make build - собрать сайт"
- @echo " make deploy - собрать и задеплоить сайт"
- @echo " make deploy-dry-run - симуляция деплоя"
+ @echo " make deploy - собрать и задеплоить сайт (SFTP через lftp, требуется SSHPASS)"
+ @echo " make deploy-dry-run - симуляция деплоя (требуется SSHPASS)"
@echo " make clean - удалить папку public"
@echo " make rebuild - полная пересборка сайта"
- @echo " make help - показать это сообщение"
\ No newline at end of file
+ @echo " make help - показать это сообщение"
+ @echo ""
+ @echo "Деплой: export SSHPASS='<пароль SFTP>'; make deploy"
+ @echo "Пароль хранится в DEPLOY_INSTRUCTIONS.md (winscp_script.txt для Windows)."
\ No newline at end of file
diff --git a/README.md b/README.md
index 5417ad1..f6f8fa3 100644
--- a/README.md
+++ b/README.md
@@ -71,22 +71,30 @@
## Деплой сайта
-Для автоматизации процесса деплоя созданы специальные файлы:
+> **ЕДИНСТВЕННЫЙ способ деплоя сайта — `make deploy`.** Не деплойте вручную
+> (rsync/ssh-exec напрямую не работают: аккаунт Jino SFTP-only).
-- `Makefile` - содержит команды для сборки и публикации сайта на хостинге (работает как на Unix-подобных системах, так и на Windows)
-- `winscp_script.txt` - скрипт для WinSCP с командами подключения и синхронизации
-- `DEPLOY_INSTRUCTIONS.md` - инструкция по использованию команд деплоя
+Специальные файлы:
-Для публикации сайта на хостинге используйте команду:
+- `Makefile` — команды сборки и публикации (Unix: SFTP через `tools/deploy_sftp.py`
+ на paramiko; Windows: WinSCP.com)
+- `tools/deploy_sftp.py` — рекурсивный mirror `public/` → `/dedinit.ru`
+ (с удалением лишнего, как `rsync --delete`), пароль из env `SSHPASS`
+- `winscp_script.txt` — скрипт для WinSCP (Windows)
+- `DEPLOY_INSTRUCTIONS.md` — инструкция по деплою
+
+Для публикации сайта на хостинге:
```shell
+export SSHPASS='<пароль SFTP из DEPLOY_INSTRUCTIONS.md>'
make deploy
```
-Для получения полного списка доступных команд используйте:
+Первая сборка создаст `.venv/` (paramiko). Для получения полного списка команд:
```shell
make help
```
-Процесс деплоя кроссплатформенный: на Unix-подобных системах используется rsync, на Windows - WinSCP.com для SFTP-подключения
+Процесс кроссплатформенный: на Unix — SFTP (paramiko), на Windows — WinSCP.com.
+НЕ удаляйте `make deploy` и не заменяйте его на прямой rsync.
diff --git a/tools/deploy_sftp.py b/tools/deploy_sftp.py
new file mode 100644
index 0000000..b7679ff
--- /dev/null
+++ b/tools/deploy_sftp.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+"""Deploy public/ -> /dedinit.ru on Jino (SFTP-only, password auth).
+
+Используется командой `make deploy` (единственный способ деплоя сайта).
+Jino-аккаунт kpa39l_dedinit — SFTP-only: удалённый exec (rsync/ssh) запрещён,
+поэтому заливаем через SFTP с рекурсивным mirror и --delete (как rsync).
+Пароль — из env SSHPASS (см. README.md / DEPLOY_INSTRUCTIONS.md).
+"""
+import os
+import stat as stmod
+import sys
+
+try:
+ import paramiko
+except ImportError:
+ print("ОШИБКА: нет paramiko. Установите: pip install paramiko", file=sys.stderr)
+ sys.exit(1)
+
+HOST = os.environ.get("DEDINIT_HOST", "kpa39l.myjino.ru")
+PORT = int(os.environ.get("DEDINIT_PORT", "2222"))
+USER = os.environ.get("DEDINIT_USER", "kpa39l_dedinit")
+PASS = os.environ.get("SSHPASS", "")
+LOCAL = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "public")
+REMOTE = os.environ.get("DEDINIT_REMOTE", "/dedinit.ru")
+DRYRUN = "--dry-run" in sys.argv
+
+if not PASS:
+ print("ОШИБКА: переменная SSHPASS (пароль SFTP) не задана. См. README.md / DEPLOY_INSTRUCTIONS.md", file=sys.stderr)
+ sys.exit(1)
+
+LOCAL = os.path.abspath(LOCAL)
+if not os.path.isdir(LOCAL):
+ print(f"ОШИБКА: нет каталога {LOCAL}. Сначала: make build", file=sys.stderr)
+ sys.exit(1)
+
+t = paramiko.Transport((HOST, PORT))
+t.connect(username=USER, password=PASS)
+sftp = paramiko.SFTPClient.from_transport(t)
+
+
+def rlist(path):
+ out = []
+ try:
+ entries = sftp.listdir_attr(path)
+ except IOError:
+ return out
+ for e in entries:
+ full = os.path.join(path, e.filename)
+ rel = os.path.relpath(full, REMOTE)
+ isdir = stmod.S_ISDIR(e.st_mode)
+ out.append((rel, isdir))
+ if isdir:
+ out.extend(rlist(full))
+ return out
+
+
+print(f"Копирую {LOCAL} -> sftp://{USER}@{HOST}:{PORT}{REMOTE}/"
+ + (" (DRY-RUN)" if DRYRUN else ""))
+
+local_files = []
+for root, dirs, files in os.walk(LOCAL):
+ for f in files:
+ local_files.append(os.path.relpath(os.path.join(root, f), LOCAL))
+
+if not DRYRUN:
+ remote_entries = rlist(REMOTE)
+ remote_files = {r for r, d in remote_entries if not d}
+
+for rel in sorted(local_files):
+ if DRYRUN:
+ print(" +", rel)
+ continue
+ lpath = os.path.join(LOCAL, rel)
+ rpath = os.path.join(REMOTE, rel)
+ rdir = os.path.dirname(rpath)
+ cur = REMOTE
+ for seg in os.path.relpath(rdir, REMOTE).split(os.sep):
+ if not seg:
+ continue
+ cur = os.path.join(cur, seg)
+ try:
+ sftp.stat(cur)
+ except IOError:
+ try:
+ sftp.mkdir(cur)
+ except IOError:
+ pass
+ sftp.put(lpath, rpath)
+ print(" +", rel)
+
+if not DRYRUN:
+ to_delete = remote_files - set(local_files)
+ for rel in sorted(to_delete):
+ try:
+ sftp.remove(os.path.join(REMOTE, rel))
+ print(" -", rel)
+ except Exception as e:
+ print(" !", rel, e)
+
+sftp.close()
+t.close()
+print("Готово." if not DRYRUN else "DRY-RUN завершён: показаны изменения без заливки.")
\ No newline at end of file