2021-09-13 12:52:50 +03:00
|
|
|
import logging
|
|
|
|
import os
|
2022-06-26 15:00:03 +03:00
|
|
|
import posixpath
|
|
|
|
from pathlib import Path
|
2021-09-13 12:52:50 +03:00
|
|
|
|
2023-05-03 08:48:49 +03:00
|
|
|
from flipper.utils import timestamp, file_md5
|
|
|
|
from flipper.utils.fstree import FsNode, compare_fs_trees
|
2021-09-13 12:52:50 +03:00
|
|
|
|
|
|
|
MANIFEST_VERSION = 0
|
|
|
|
|
|
|
|
|
|
|
|
class ManifestRecord:
|
|
|
|
tag = None
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def fromLine(line):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def toLine(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2022-04-19 22:02:37 +03:00
|
|
|
def _unpack(self, manifest, key, nodetype):
|
2021-09-13 12:52:50 +03:00
|
|
|
key, value = manifest.readline().split(":", 1)
|
|
|
|
assert key == key
|
2022-04-19 22:02:37 +03:00
|
|
|
return nodetype(value)
|
2021-09-13 12:52:50 +03:00
|
|
|
|
|
|
|
|
|
|
|
MANIFEST_TAGS_RECORDS = {}
|
|
|
|
|
|
|
|
|
|
|
|
def addManifestRecord(record: ManifestRecord):
|
|
|
|
assert record.tag
|
|
|
|
MANIFEST_TAGS_RECORDS[record.tag] = record
|
|
|
|
|
|
|
|
|
|
|
|
class ManifestRecordVersion(ManifestRecord):
|
|
|
|
tag = "V"
|
|
|
|
|
|
|
|
def __init__(self, version):
|
|
|
|
self.version = version
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def fromLine(line):
|
|
|
|
return ManifestRecordVersion(int(line))
|
|
|
|
|
|
|
|
def toLine(self):
|
|
|
|
return f"{self.tag}:{self.version}\n"
|
|
|
|
|
|
|
|
|
|
|
|
addManifestRecord(ManifestRecordVersion)
|
|
|
|
|
|
|
|
|
|
|
|
class ManifestRecordTimestamp(ManifestRecord):
|
|
|
|
tag = "T"
|
|
|
|
|
|
|
|
def __init__(self, timestamp: int):
|
|
|
|
self.timestamp = int(timestamp)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def fromLine(line):
|
|
|
|
return ManifestRecordTimestamp(int(line))
|
|
|
|
|
|
|
|
def toLine(self):
|
|
|
|
return f"{self.tag}:{self.timestamp}\n"
|
|
|
|
|
|
|
|
|
|
|
|
addManifestRecord(ManifestRecordTimestamp)
|
|
|
|
|
|
|
|
|
|
|
|
class ManifestRecordDirectory(ManifestRecord):
|
|
|
|
tag = "D"
|
|
|
|
|
|
|
|
def __init__(self, path: str):
|
|
|
|
self.path = path
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def fromLine(line):
|
|
|
|
return ManifestRecordDirectory(line)
|
|
|
|
|
|
|
|
def toLine(self):
|
|
|
|
return f"{self.tag}:{self.path}\n"
|
|
|
|
|
|
|
|
|
|
|
|
addManifestRecord(ManifestRecordDirectory)
|
|
|
|
|
|
|
|
|
|
|
|
class ManifestRecordFile(ManifestRecord):
|
|
|
|
tag = "F"
|
|
|
|
|
|
|
|
def __init__(self, path: str, md5: str, size: int):
|
|
|
|
self.path = path
|
|
|
|
self.md5 = md5
|
|
|
|
self.size = size
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def fromLine(line):
|
|
|
|
data = line.split(":", 3)
|
2022-04-19 22:02:37 +03:00
|
|
|
return ManifestRecordFile(data[2], data[0], int(data[1]))
|
2021-09-13 12:52:50 +03:00
|
|
|
|
|
|
|
def toLine(self):
|
|
|
|
return f"{self.tag}:{self.md5}:{self.size}:{self.path}\n"
|
|
|
|
|
|
|
|
|
|
|
|
addManifestRecord(ManifestRecordFile)
|
|
|
|
|
|
|
|
|
|
|
|
class Manifest:
|
2022-09-29 14:00:22 +03:00
|
|
|
def __init__(self, timestamp_value=None):
|
2021-09-13 12:52:50 +03:00
|
|
|
self.version = None
|
|
|
|
self.records = []
|
|
|
|
self.records.append(ManifestRecordVersion(MANIFEST_VERSION))
|
2022-09-29 14:00:22 +03:00
|
|
|
self.records.append(ManifestRecordTimestamp(timestamp_value or timestamp()))
|
2021-09-13 12:52:50 +03:00
|
|
|
self.logger = logging.getLogger(self.__class__.__name__)
|
|
|
|
|
|
|
|
def load(self, filename):
|
2022-06-26 15:00:03 +03:00
|
|
|
with open(filename, "r") as manifest:
|
|
|
|
for line in manifest.readlines():
|
|
|
|
line = line.strip()
|
|
|
|
if len(line) == 0:
|
|
|
|
continue
|
|
|
|
tag, line = line.split(":", 1)
|
|
|
|
record = MANIFEST_TAGS_RECORDS[tag].fromLine(line)
|
|
|
|
self.records.append(record)
|
2021-09-13 12:52:50 +03:00
|
|
|
|
|
|
|
def save(self, filename):
|
2022-06-26 15:00:03 +03:00
|
|
|
with open(filename, "w+", newline="\n") as manifest:
|
|
|
|
for record in self.records:
|
|
|
|
manifest.write(record.toLine())
|
2021-09-13 12:52:50 +03:00
|
|
|
|
|
|
|
def addDirectory(self, path):
|
|
|
|
self.records.append(ManifestRecordDirectory(path))
|
|
|
|
|
|
|
|
def addFile(self, path, md5, size):
|
|
|
|
self.records.append(ManifestRecordFile(path, md5, size))
|
|
|
|
|
2022-04-19 22:02:37 +03:00
|
|
|
def create(self, directory_path, ignore_files=["Manifest"]):
|
2021-09-13 12:52:50 +03:00
|
|
|
for root, dirs, files in os.walk(directory_path):
|
2022-04-27 19:30:37 +03:00
|
|
|
dirs.sort()
|
|
|
|
files.sort()
|
2021-09-13 12:52:50 +03:00
|
|
|
relative_root = root.replace(directory_path, "", 1)
|
2022-06-26 15:00:03 +03:00
|
|
|
if relative_root:
|
|
|
|
relative_root = Path(relative_root).as_posix()
|
2021-09-13 12:52:50 +03:00
|
|
|
if relative_root.startswith("/"):
|
|
|
|
relative_root = relative_root[1:]
|
|
|
|
# process directories
|
2022-06-26 15:00:03 +03:00
|
|
|
for dirname in dirs:
|
|
|
|
relative_dir_path = posixpath.join(relative_root, dirname)
|
2022-04-19 22:02:37 +03:00
|
|
|
self.logger.debug(f'Adding directory: "{relative_dir_path}"')
|
2021-09-13 12:52:50 +03:00
|
|
|
self.addDirectory(relative_dir_path)
|
|
|
|
# Process files
|
|
|
|
for file in files:
|
2022-06-26 15:00:03 +03:00
|
|
|
relative_file_path = posixpath.join(relative_root, file)
|
2022-04-19 22:02:37 +03:00
|
|
|
if file in ignore_files:
|
|
|
|
self.logger.info(f'Skipping file "{relative_file_path}"')
|
|
|
|
continue
|
2022-06-26 15:00:03 +03:00
|
|
|
full_file_path = posixpath.join(root, file)
|
2022-04-19 22:02:37 +03:00
|
|
|
self.logger.debug(f'Adding file: "{relative_file_path}"')
|
2021-09-13 12:52:50 +03:00
|
|
|
self.addFile(
|
|
|
|
relative_file_path,
|
|
|
|
file_md5(full_file_path),
|
|
|
|
os.path.getsize(full_file_path),
|
|
|
|
)
|
|
|
|
|
|
|
|
def toFsTree(self):
|
2022-04-19 22:02:37 +03:00
|
|
|
root = FsNode("", FsNode.NodeType.Directory)
|
2021-09-13 12:52:50 +03:00
|
|
|
for record in self.records:
|
|
|
|
if isinstance(record, ManifestRecordDirectory):
|
|
|
|
root.addDirectory(record.path)
|
|
|
|
elif isinstance(record, ManifestRecordFile):
|
|
|
|
root.addFile(record.path, record.md5, record.size)
|
|
|
|
return root
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def compare(left: "Manifest", right: "Manifest"):
|
|
|
|
return compare_fs_trees(left.toFsTree(), right.toFsTree())
|