Я вообще не очень в теме, как стоило решить подобную задачу, но ко мне пришло такое решение и я хочу поделиться им с комьюнити, и, надеюсь, на адекватную критику и всё такое. Суть задачи - нужно загружать из определённой директории (директорий) файлы через scp, если директории нет - создать её. По мере развития проекта файлов и директорий становится много, потому нужно проверять, во-первых, не создана ли ещё директория (чтобы лишний раз не дёргать сервер), во-вторых, был ли изменён файл (чтобы лишний раз не гонять его по сети). Для хранения данных используются два файла: для информации о загруженных файлах и о созданных директориях. Собственно, вот код на третьем питоне. Язык я знаю крайне паршиво, так что, вероятно, можно было написать всё покороче... но зато вроде всё работает.
# -*- coding: utf-8 -*-
import os
from pathlib import Path
import paramiko
from paramiko import SSHClient
from scp import SCPClient
SERVER_IP = "192.168.0.1"
SERVER_USERNAME = "root"
SERVER_PASSWORD = "toor"
SERVER_DIRECTORY = '/var/www/html'
CACHE_FILES = "uploader-cache-files.txt"
CACHE_DIRS = "uploader-cache-dirs.txt"
def check_file_changes(name: str) -> bool:
	my_file = Path(CACHE_FILES)
	if not my_file.is_file():
		f = open(CACHE_FILES, "w+")
		f.close()
	f = open(CACHE_FILES, "r")
	lines = []
	while True:
		line = f.readline()
		if not line:
			f.close()
			break
		lines.append(line)
	need_load = False
	found = False
	f = open(CACHE_FILES, "w")
	for line in lines:
		pair = line.split('|')
		file_name = pair[0]
		file_size = int(pair[1])
		statinfo = os.stat(file_name)
		if file_name == name:
			found = True
			if statinfo.st_size != file_size:
				line = file_name + '|' + str(statinfo.st_size) + '\r\n'
				need_load = True
		f.write(line)
	if not found:
		need_load = True
		statinfo = os.stat(name)
		line = name + '|' + str(statinfo.st_size) + '\r\n'
		f.write(line)
	f.close()
	return need_load
def need_create_dir(name: str) -> bool:
	my_file = Path(CACHE_DIRS)
	if not my_file.is_file():
		f = open(CACHE_DIRS, "w+")
		f.close()
	f = open(CACHE_DIRS, "r")
	lines = []
	while True:
		line = f.readline()
		if not line:
			f.close()
			break
		lines.append(line)
	found = False
	f = open(CACHE_DIRS, "w")
	for line in lines:
		if line.strip() == name:
			found = True
		f.write(line.strip() + '\r\n')
	if not found:
		f.write(name + '\r\n')
	return not found
def need_upload_file(name: str) -> bool:
	if name == '.htaccess':
		return True
	ext = name.split('.')
	ext = ext[len(ext) - 1]
	if ext in ['php', 'css', 'js', 'png', 'jpg', 'gif', 'sql']:
		return True
	return False
def upload_file(name: str, path: str, scp: SCPClient):
	if check_file_changes(name):
		print("File '" + name + "' uploading...")
		scp.put(name, path)
	else:
		print("File '" + name + "' skip")
def upload_directory(name: str, ssh: SSHClient, scp: SCPClient):
	print("Observe '" + name + "' directory...")
	if name != '.' and need_create_dir(name):
		ssh.exec_command('mkdir -p ' + SERVER_DIRECTORY + '/' + name)
	files = [f for f in os.listdir(name) if os.path.isfile(name + '/' + f)]
	for f in files:
		if need_upload_file(f):
			upload_file(name + '/' + f, SERVER_DIRECTORY + '/' + name, scp)
	dirs = [d for d in os.listdir(name) if os.path.isdir(name + '/' + d)]
	for d in dirs:
		if d not in ['.', '..', '.git', '__pycache__']:
			upload_directory(name + '/' + d, ssh, scp)
def upload():
	ssh = SSHClient()
	ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
	ssh.connect(SERVER_IP, 22, SERVER_USERNAME, SERVER_PASSWORD)
	scp = SCPClient(ssh.get_transport())
	upload_directory('app', ssh, scp)
	scp.close()
	ssh.close()
if __name__ == "__main__":
	upload()






