chore(deploy): make deploy — единственный способ деплоя (SFTP paramiko вместо rsync)
- Makefile Unix: tools/deploy_sftp.py (paramiko mirror --delete), autovenv - tools/deploy_sftp.py: рекурсивный SFTP-mirror public/ -> /dedinit.ru - Jino SFTP-only: rsync/ssh-exec запрещены (exec request failed on channel 0) - README/DEPLOY_INSTRUCTIONS: обновлены, деплой только через make deploy - .gitignore: + .venv/
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
/public/
|
/public/
|
||||||
|
.venv/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
|||||||
+34
-25
@@ -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
|
- **Хост**: kpa39l.myjino.ru
|
||||||
- **Порт SFTP**: 2222
|
- **Порт SFTP**: 2222
|
||||||
- **Пользователь**: kpa39l_dedinit
|
- **Пользователь**: kpa39l_dedinit
|
||||||
- **Пароль**: -Zp4ep747t25
|
- **Пароль**: -Zp4ep747t25
|
||||||
- **Целевой путь**: /dedinit.ru (относительно домашнего каталога пользователя)
|
- **Целевой путь**: /dedinit.ru (относительно корня аккаунта)
|
||||||
|
|
||||||
## Установка зависимостей
|
## Установка зависимостей
|
||||||
|
|
||||||
### Для Linux/macOS
|
### Для Linux/macOS
|
||||||
|
|
||||||
- **rsync** - установите через менеджер пакетов (например, apt-get install rsync или brew install rsync)
|
- **Python 3** (с venv/pip)
|
||||||
- **Make** - для выполнения команд из Makefile
|
- **Make** — для выполнения команд из Makefile
|
||||||
|
- Пакет `paramiko` ставится автоматически в `.venv/` при первом `make deploy`
|
||||||
|
(таргет `venv`), либо вручную: `pip install paramiko`
|
||||||
|
|
||||||
### Для Windows
|
### Для Windows
|
||||||
|
|
||||||
- **WinSCP** - установите с официального сайта (<https://winscp.net/>)
|
- **WinSCP** — установите с официального сайта (<https://winscp.net/>)
|
||||||
- **Make** - можно установить через Chocolatey, MinGW или Git for Windows
|
- **Make** — можно установить через Chocolatey, MinGW или Git for Windows
|
||||||
|
|
||||||
## Использование Makefile
|
## Использование Makefile
|
||||||
|
|
||||||
@@ -39,23 +46,28 @@ make build
|
|||||||
### Деплой сайта
|
### Деплой сайта
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# один раз экспортируем пароль SFTP (каждый раз в новой сессии)
|
||||||
|
export SSHPASS='-Zp4ep747t25'
|
||||||
make deploy
|
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
|
```bash
|
||||||
|
export SSHPASS='-Zp4ep747t25'
|
||||||
make deploy-dry-run
|
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
|
||||||
|
|
||||||
Для корректной работы на Windows необходимо:
|
1. Убедиться, что WinSCP установлен в стандартную директорию
|
||||||
|
`C:\Program Files (x86)\WinSCP\`
|
||||||
1. Убедиться, что WinSCP установлен в стандартную директорию `C:\Program Files (x86)\WinSCP\`
|
2. Файл `winscp_script.txt` содержит команды для подключения и синхронизации файлов
|
||||||
2. Файл `winscp_script.txt` содержит команды для подключения и синхронизации файлов
|
|
||||||
@@ -7,35 +7,48 @@ ifeq ($(OS),Windows_NT)
|
|||||||
DRYRUN_CMD = echo "Windows: проверка SFTP-соединения..."
|
DRYRUN_CMD = echo "Windows: проверка SFTP-соединения..."
|
||||||
else
|
else
|
||||||
PLATFORM = Unix
|
PLATFORM = Unix
|
||||||
# На Unix-подобных системах используем rsync для SFTP
|
# На Unix используем paramiko (SFTP). Аккаунт Jino — SFTP-only: rsync/ssh-exec
|
||||||
HOST = kpa39l.myjino.ru
|
# запрещены ("exec request failed on channel 0"), поэтому deploy через
|
||||||
PORT = 2222
|
# tools/deploy_sftp.py (рекурсивный mirror + удаление лишнего).
|
||||||
USERNAME = kpa39l_dedinit
|
# Венв .venv создаётся автоматически таргетом venv (нужен paramiko).
|
||||||
REMOTE_PATH = /dedinit.ru
|
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
|
endif
|
||||||
.PHONY: build deploy deploy-dry-run clean rebuild help
|
.PHONY: build deploy deploy-dry-run clean rebuild help venv
|
||||||
|
|
||||||
build:
|
build:
|
||||||
hugo --minify
|
hugo --minify
|
||||||
@echo "Сайт успешно собран в папку public/"
|
@echo "Сайт успешно собран в папку public/"
|
||||||
|
|
||||||
deploy: build
|
# ЕДИНСТВЕННЫЙ способ деплоя — через make deploy.
|
||||||
|
# Никогда не заменять на rsync/ssh-exec напрямую: на Jino аккаунт SFTP-only.
|
||||||
|
deploy: build venv
|
||||||
ifeq ($(PLATFORM),Windows)
|
ifeq ($(PLATFORM),Windows)
|
||||||
$(DEPLOY_CMD)
|
$(DEPLOY_CMD)
|
||||||
else
|
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
|
endif
|
||||||
|
|
||||||
deploy-dry-run: build
|
deploy-dry-run: build venv
|
||||||
ifeq ($(PLATFORM),Windows)
|
ifeq ($(PLATFORM),Windows)
|
||||||
@echo "Выполняем проверку соединения с сервером..."
|
@echo "Выполняем проверку соединения с сервером..."
|
||||||
$(DRYRUN_CMD)
|
$(DRYRUN_CMD)
|
||||||
else
|
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
|
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:
|
clean:
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
del /q /s public\
|
del /q /s public\/
|
||||||
else
|
else
|
||||||
rm -rf public/
|
rm -rf public/
|
||||||
endif
|
endif
|
||||||
@@ -46,8 +59,11 @@ rebuild: clean deploy
|
|||||||
help:
|
help:
|
||||||
@echo "Доступные команды:"
|
@echo "Доступные команды:"
|
||||||
@echo " make build - собрать сайт"
|
@echo " make build - собрать сайт"
|
||||||
@echo " make deploy - собрать и задеплоить сайт"
|
@echo " make deploy - собрать и задеплоить сайт (SFTP через lftp, требуется SSHPASS)"
|
||||||
@echo " make deploy-dry-run - симуляция деплоя"
|
@echo " make deploy-dry-run - симуляция деплоя (требуется SSHPASS)"
|
||||||
@echo " make clean - удалить папку public"
|
@echo " make clean - удалить папку public"
|
||||||
@echo " make rebuild - полная пересборка сайта"
|
@echo " make rebuild - полная пересборка сайта"
|
||||||
@echo " make help - показать это сообщение"
|
@echo " make help - показать это сообщение"
|
||||||
|
@echo ""
|
||||||
|
@echo "Деплой: export SSHPASS='<пароль SFTP>'; make deploy"
|
||||||
|
@echo "Пароль хранится в DEPLOY_INSTRUCTIONS.md (winscp_script.txt для Windows)."
|
||||||
@@ -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
|
```shell
|
||||||
|
export SSHPASS='<пароль SFTP из DEPLOY_INSTRUCTIONS.md>'
|
||||||
make deploy
|
make deploy
|
||||||
```
|
```
|
||||||
|
|
||||||
Для получения полного списка доступных команд используйте:
|
Первая сборка создаст `.venv/` (paramiko). Для получения полного списка команд:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
make help
|
make help
|
||||||
```
|
```
|
||||||
|
|
||||||
Процесс деплоя кроссплатформенный: на Unix-подобных системах используется rsync, на Windows - WinSCP.com для SFTP-подключения
|
Процесс кроссплатформенный: на Unix — SFTP (paramiko), на Windows — WinSCP.com.
|
||||||
|
НЕ удаляйте `make deploy` и не заменяйте его на прямой rsync.
|
||||||
|
|||||||
@@ -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 завершён: показаны изменения без заливки.")
|
||||||
Reference in New Issue
Block a user