From ecb305c6fdf43944a9082870474d95041df7dcf0 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Tue, 21 Nov 2023 11:13:16 +0100 Subject: archiver: New --- rtemsspec/archiver.py | 252 +++++++++++++++++++++ rtemsspec/packagebuildfactory.py | 2 + .../spec-packagebuild/qdp/deployment/archive.yml | 13 ++ .../qdp/deployment/verify-package.yml | 13 ++ .../tests/spec-packagebuild/qdp/package-build.yml | 2 + rtemsspec/tests/spec-packagebuild/qdp/source/a.yml | 19 ++ rtemsspec/tests/spec-packagebuild/qdp/source/b.yml | 23 ++ rtemsspec/tests/spec-packagebuild/qdp/source/e.yml | 19 ++ .../tests/spec-packagebuild/qdp/steps/archive.yml | 23 ++ rtemsspec/tests/test-files/dir/a.txt | 1 + rtemsspec/tests/test-files/dir/b.txt | 1 + rtemsspec/tests/test-files/dir/e.txt | 1 + rtemsspec/tests/test-files/dir/subdir/c.txt | 1 + rtemsspec/tests/test-files/dir/subdir/d.txt | 1 + rtemsspec/tests/test_packagebuild.py | 45 +++- spec-qdp/spec/qdp-archive.yml | 58 +++++ 16 files changed, 472 insertions(+), 2 deletions(-) create mode 100644 rtemsspec/archiver.py create mode 100644 rtemsspec/tests/spec-packagebuild/qdp/deployment/archive.yml create mode 100644 rtemsspec/tests/spec-packagebuild/qdp/deployment/verify-package.yml create mode 100644 rtemsspec/tests/spec-packagebuild/qdp/source/a.yml create mode 100644 rtemsspec/tests/spec-packagebuild/qdp/source/b.yml create mode 100644 rtemsspec/tests/spec-packagebuild/qdp/source/e.yml create mode 100644 rtemsspec/tests/spec-packagebuild/qdp/steps/archive.yml create mode 100644 rtemsspec/tests/test-files/dir/a.txt create mode 100644 rtemsspec/tests/test-files/dir/b.txt create mode 100644 rtemsspec/tests/test-files/dir/e.txt create mode 100644 rtemsspec/tests/test-files/dir/subdir/c.txt create mode 100644 rtemsspec/tests/test-files/dir/subdir/d.txt create mode 100644 spec-qdp/spec/qdp-archive.yml diff --git a/rtemsspec/archiver.py b/rtemsspec/archiver.py new file mode 100644 index 00000000..b6aa8f2f --- /dev/null +++ b/rtemsspec/archiver.py @@ -0,0 +1,252 @@ +# SPDX-License-Identifier: BSD-2-Clause +""" Build step to package deployed components into archive. """ + +# Copyright (C) 2021 EDISOFT (https://www.edisoft.pt/) +# Copyright (C) 2020, 2021 embedded brains GmbH & Co. KG +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import logging +import os +import stat +import tarfile +from typing import cast, Dict, List + +from rtemsspec.packagebuild import BuildItem +from rtemsspec.directorystate import DirectoryState + + +def _check_for_duplicates(uid: str, members: List[DirectoryState]) -> None: + logging.info("%s: check for duplicate files", uid) + for index, dir_state in enumerate(members): + logging.debug("%s: get files of: %s", uid, dir_state.uid) + files = dict(dir_state.files_and_hashes()) + paths = set(files.keys()) + for file_path in files: + assert os.path.isfile(file_path) or os.path.islink(file_path) + for dir_state_2 in members[index + 1:]: + logging.debug("%s: compare with files of: %s", uid, + dir_state_2.uid) + files_2 = dict(dir_state_2.files_and_hashes()) + paths_2 = set(files_2.keys()) + duplicates = paths.intersection(paths_2) + if duplicates: + logging.info( + "%s: duplicate files in directory states " + "%s and %s", uid, dir_state.uid, dir_state_2.uid) + for file_path in duplicates: + logging.info("%s: duplicate file: %s", uid, file_path) + value = files[file_path] + value_2 = files_2[file_path] + if value == value_2: + continue + logging.error( + "%s: inconsistent file hashes for '%s': %s != %s", uid, + file_path, value, value_2) + + +_SCRIPT_HEAD = """#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-2-Clause +\"\"\" Verifies the files of the package. \"\"\" + +# Copyright (C) 2021, 2022 embedded brains GmbH & Co. KG +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import base64 +import binascii +import argparse +import hashlib +import logging +import os +import sys +from typing import Dict, List + + +_FILES = { +""" + +_SCRIPT_TAIL = """} + + +def _hash_file(path: str) -> str: + file_hash = hashlib.sha512() + if os.path.islink(path): + file_hash.update(os.readlink(path).encode("utf-8")) + else: + buf = bytearray(65536) + memview = memoryview(buf) + with open(path, "rb", buffering=0) as src: + for size in iter(lambda: src.readinto(memview), 0): # type: ignore + file_hash.update(memview[:size]) + return base64.urlsafe_b64encode(file_hash.digest()).decode("ascii") + + +def _hex(digest: str) -> str: + binary = base64.urlsafe_b64decode(digest) + return binascii.hexlify(binary).decode('ascii') + + +def _check_file(file_path: str, expected_files: Dict[str, str]) -> int: + expected_hash = expected_files[file_path] + actual_hash = _hash_file(file_path) + if expected_hash != actual_hash: + logging.error( + "expected hash is %s, actual hash is %s for file: %s", + _hex(expected_hash), _hex(actual_hash), file_path) + return 1 + return 0 + + +def _verify_files(script: str, expected_files: Dict[str, str]) -> int: + status = 0 + script = os.path.normpath(script) + for path, dirs, files in os.walk("."): + dirs.sort() + for name in sorted(files): + file_path = os.path.normpath(os.path.join(path, name)) + if file_path in expected_files: + status = _check_file(file_path, expected_files) + del expected_files[file_path] + elif file_path != script: + logging.warning("unexpected file: %s", file_path) + for maybe_missing in expected_files.keys(): + if os.path.islink(maybe_missing): + status = _check_file(maybe_missing, expected_files) + continue + logging.error("missing file: %s", maybe_missing) + status = 1 + return status + + +def main(script: str, argv: List[str]) -> int: + \"\"\" Verifies the files of the package. \"\"\" + parser = argparse.ArgumentParser() + parser.add_argument( + "--log-level", + choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], + type=str.upper, + default="WARNING", + help="log level") + parser.add_argument("--log-file", + type=str, + default=None, + help="log to this file") + parser.add_argument("--list-files", + action="store_true", + help="list the files of the package") + parser.add_argument("--list-files-and-hashes", + action="store_true", + help="list the files of the package " + "with the SHA512 digest of each file") + args = parser.parse_args(argv) + logging.basicConfig(filename=args.log_file, level=args.log_level) + expected_files = dict( + zip(map(lambda x: os.path.normpath(x), _FILES.keys()), + _FILES.values())) + status = 0 + if args.list_files_and_hashes: + for file_path, hash_value in expected_files.items(): + print(f"{file_path}\t{_hex(hash_value)}") + elif args.list_files: + for file_path in expected_files.keys(): + print(file_path) + else: + status = _verify_files(script, expected_files) + return status + + + +if __name__ == "__main__": + status = main(sys.argv[0], sys.argv[1:]) + sys.exit(status) +""" + + +def _create_verification_script(script: str, archive_files: Dict[str, + str]) -> None: + with open(script, "w", encoding="utf-8") as out: + out.write(_SCRIPT_HEAD) + for file_path, hash_value in sorted(archive_files.items()): + print(f" \"{file_path}\": \"{hash_value}\",", file=out) + out.write(_SCRIPT_TAIL) + os.chmod(script, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + +class Archiver(BuildItem): + """ + The archiver adds the file of its directory state dependencies to an + archive file. + """ + + def run(self) -> None: + archive_file = self["archive-file"] + archive_state = self.output("archive") + assert isinstance(archive_state, DirectoryState) + archive_state.set_files([archive_file]) + archive_file = os.path.join(archive_state.directory, archive_file) + script_file = self["verification-script"] + script_state = self.output("verify-package") + assert isinstance(script_state, DirectoryState) + script_state.set_files([script_file]) + script_file = os.path.join(script_state.directory, script_file) + script_dir = os.path.dirname(script_file) + logging.info("%s: create archive: %s", self.uid, archive_file) + os.makedirs(os.path.dirname(archive_file), exist_ok=True) + with tarfile.open(archive_file, "w:xz") as tar_file: + members = cast(List[DirectoryState], list(self.inputs("member"))) + _check_for_duplicates(self.uid, members) + strip_prefix = self["archive-strip-prefix"] + archive_files: Dict[str, str] = {} + for dir_state in members: + logging.info("%s: add files of directory state: %s", self.uid, + dir_state.uid) + for file_path, hash_value in dir_state.files_and_hashes(): + verify_path = os.path.relpath(file_path, script_dir) + assert hash_value + archive_files[verify_path] = hash_value + tar_file.add(file_path, + os.path.relpath(file_path, strip_prefix)) + _create_verification_script(script_file, archive_files) + tar_file.add(script_file, os.path.relpath(script_file, + strip_prefix)) + logging.info("%s: finished to create archive: %s", self.uid, + archive_file) diff --git a/rtemsspec/packagebuildfactory.py b/rtemsspec/packagebuildfactory.py index 8cd430ab..ef4a950b 100644 --- a/rtemsspec/packagebuildfactory.py +++ b/rtemsspec/packagebuildfactory.py @@ -24,6 +24,7 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +from rtemsspec.archiver import Archiver from rtemsspec.directorystate import DirectoryState from rtemsspec.packagebuild import BuildItemFactory, PackageVariant @@ -31,6 +32,7 @@ from rtemsspec.packagebuild import BuildItemFactory, PackageVariant def create_build_item_factory() -> BuildItemFactory: """ Creates the default build item factory. """ factory = BuildItemFactory() + factory.add_constructor("qdp/build-step/archive", Archiver) factory.add_constructor("qdp/directory-state/generic", DirectoryState) factory.add_constructor("qdp/directory-state/repository", DirectoryState) factory.add_constructor("qdp/directory-state/unpacked-archive", diff --git a/rtemsspec/tests/spec-packagebuild/qdp/deployment/archive.yml b/rtemsspec/tests/spec-packagebuild/qdp/deployment/archive.yml new file mode 100644 index 00000000..823071e8 --- /dev/null +++ b/rtemsspec/tests/spec-packagebuild/qdp/deployment/archive.yml @@ -0,0 +1,13 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2020 embedded brains GmbH & Co. KG +copyrights-by-license: {} +directory: ${../variant:/build-directory} +directory-state-type: generic +enabled-by: true +files: [] +hash: null +links: [] +patterns: [] +qdp-type: directory-state +type: qdp diff --git a/rtemsspec/tests/spec-packagebuild/qdp/deployment/verify-package.yml b/rtemsspec/tests/spec-packagebuild/qdp/deployment/verify-package.yml new file mode 100644 index 00000000..a6b8c747 --- /dev/null +++ b/rtemsspec/tests/spec-packagebuild/qdp/deployment/verify-package.yml @@ -0,0 +1,13 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2021 embedded brains GmbH & Co. KG +copyrights-by-license: {} +directory: ${../variant:/prefix-directory} +directory-state-type: generic +enabled-by: true +files: [] +hash: null +links: [] +patterns: [] +qdp-type: directory-state +type: qdp diff --git a/rtemsspec/tests/spec-packagebuild/qdp/package-build.yml b/rtemsspec/tests/spec-packagebuild/qdp/package-build.yml index 1b451572..4e72be8b 100644 --- a/rtemsspec/tests/spec-packagebuild/qdp/package-build.yml +++ b/rtemsspec/tests/spec-packagebuild/qdp/package-build.yml @@ -9,5 +9,7 @@ links: uid: steps/b - role: build-step uid: steps/c +- role: build-step + uid: steps/archive qdp-type: package-build type: qdp diff --git a/rtemsspec/tests/spec-packagebuild/qdp/source/a.yml b/rtemsspec/tests/spec-packagebuild/qdp/source/a.yml new file mode 100644 index 00000000..498a5546 --- /dev/null +++ b/rtemsspec/tests/spec-packagebuild/qdp/source/a.yml @@ -0,0 +1,19 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2023 embedded brains GmbH & Co. KG +copyrights-by-license: {} +directory: ${../variant:/prefix-directory} +directory-state-type: generic +enabled-by: true +files: +- file: dir/a.txt + hash: null +- file: dir/subdir/c.txt + hash: null +- file: dir/subdir/d.txt + hash: null +hash: null +links: [] +patterns: [] +qdp-type: directory-state +type: qdp diff --git a/rtemsspec/tests/spec-packagebuild/qdp/source/b.yml b/rtemsspec/tests/spec-packagebuild/qdp/source/b.yml new file mode 100644 index 00000000..f2836d4e --- /dev/null +++ b/rtemsspec/tests/spec-packagebuild/qdp/source/b.yml @@ -0,0 +1,23 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2023 embedded brains GmbH & Co. KG +copyrights-by-license: {} +directory: ${../variant:/prefix-directory} +directory-state-type: generic +enabled-by: true +files: +- file: dir/b.txt + hash: null +- file: dir/subdir/c.txt + hash: null +- file: dir/subdir/d.txt + hash: null +hash: null +links: +- hash: null + name: member + role: input-to + uid: ../steps/archive +patterns: [] +qdp-type: directory-state +type: qdp diff --git a/rtemsspec/tests/spec-packagebuild/qdp/source/e.yml b/rtemsspec/tests/spec-packagebuild/qdp/source/e.yml new file mode 100644 index 00000000..bcae7c2a --- /dev/null +++ b/rtemsspec/tests/spec-packagebuild/qdp/source/e.yml @@ -0,0 +1,19 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2023 embedded brains GmbH & Co. KG +copyrights-by-license: {} +directory: ${../variant:/prefix-directory} +directory-state-type: generic +enabled-by: true +files: +- file: dir/e.txt + hash: null +hash: null +links: +- hash: null + name: member + role: input-to + uid: ../steps/archive +patterns: [] +qdp-type: directory-state +type: qdp diff --git a/rtemsspec/tests/spec-packagebuild/qdp/steps/archive.yml b/rtemsspec/tests/spec-packagebuild/qdp/steps/archive.yml new file mode 100644 index 00000000..e7f31cf8 --- /dev/null +++ b/rtemsspec/tests/spec-packagebuild/qdp/steps/archive.yml @@ -0,0 +1,23 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +archive-file: archive.tar.xz +archive-strip-prefix: ${../variant:/prefix-directory}/ +build-step-type: archive +copyrights: +- Copyright (C) 2023 embedded brains GmbH & Co. KG +description: | + Description. +enabled-by: archive +links: +- hash: null + name: member + role: input + uid: ../source/a +- name: archive + role: output + uid: ../deployment/archive +- name: verify-package + role: output + uid: ../deployment/verify-package +qdp-type: build-step +type: qdp +verification-script: verify_package.py diff --git a/rtemsspec/tests/test-files/dir/a.txt b/rtemsspec/tests/test-files/dir/a.txt new file mode 100644 index 00000000..f70f10e4 --- /dev/null +++ b/rtemsspec/tests/test-files/dir/a.txt @@ -0,0 +1 @@ +A diff --git a/rtemsspec/tests/test-files/dir/b.txt b/rtemsspec/tests/test-files/dir/b.txt new file mode 100644 index 00000000..223b7836 --- /dev/null +++ b/rtemsspec/tests/test-files/dir/b.txt @@ -0,0 +1 @@ +B diff --git a/rtemsspec/tests/test-files/dir/e.txt b/rtemsspec/tests/test-files/dir/e.txt new file mode 100644 index 00000000..1c507261 --- /dev/null +++ b/rtemsspec/tests/test-files/dir/e.txt @@ -0,0 +1 @@ +E diff --git a/rtemsspec/tests/test-files/dir/subdir/c.txt b/rtemsspec/tests/test-files/dir/subdir/c.txt new file mode 100644 index 00000000..3cc58df8 --- /dev/null +++ b/rtemsspec/tests/test-files/dir/subdir/c.txt @@ -0,0 +1 @@ +C diff --git a/rtemsspec/tests/test-files/dir/subdir/d.txt b/rtemsspec/tests/test-files/dir/subdir/d.txt new file mode 100644 index 00000000..17848105 --- /dev/null +++ b/rtemsspec/tests/test-files/dir/subdir/d.txt @@ -0,0 +1 @@ +D diff --git a/rtemsspec/tests/test_packagebuild.py b/rtemsspec/tests/test_packagebuild.py index 48ee2c84..436c6f29 100644 --- a/rtemsspec/tests/test_packagebuild.py +++ b/rtemsspec/tests/test_packagebuild.py @@ -29,6 +29,7 @@ import os import pytest from pathlib import Path import shutil +import tarfile from rtemsspec.items import EmptyItem, Item, ItemCache, ItemGetValueContext from rtemsspec.packagebuild import BuildItem, BuildItemMapper, \ @@ -36,6 +37,7 @@ from rtemsspec.packagebuild import BuildItem, BuildItemMapper, \ from rtemsspec.packagebuildfactory import create_build_item_factory from rtemsspec.specverify import verify from rtemsspec.tests.util import get_and_clear_log +from rtemsspec.util import run_command def _copy_dir(src, dst): @@ -50,9 +52,10 @@ def _copy_dir(src, dst): def _create_item_cache(tmp_dir: Path, spec_dir: Path) -> ItemCache: - spec_dst = tmp_dir / Path("pkg/build/spec") + spec_dst = tmp_dir / "pkg" / "build" / "spec" test_dir = Path(__file__).parent _copy_dir(test_dir / spec_dir, spec_dst) + _copy_dir(test_dir / "test-files", tmp_dir) _copy_dir(test_dir.parent.parent / "spec-spec", spec_dst) _copy_dir(test_dir.parent.parent / "spec-qdp" / "spec", spec_dst / "spec") cache_dir = os.path.join(tmp_dir, "cache") @@ -96,7 +99,8 @@ def test_packagebuild(caplog, tmpdir): factory.add_get_value("qdp/variant:/tmpdir", get_tmpdir) director = PackageBuildDirector(item_cache, factory) director.clear() - prefix_dir = Path(director["/qdp/variant"]["prefix-directory"]) + variant = director["/qdp/variant"] + prefix_dir = Path(variant["prefix-directory"]) director.build_package(None, None) log = get_and_clear_log(caplog) @@ -158,3 +162,40 @@ def test_packagebuild(caplog, tmpdir): c.output("moo") assert c["values"]["list"] == ["a", "b1", "b2", ["d", "e"], "c"] c.clear() + + # Test Archiver + dir_state_a = director["/qdp/source/a"] + dir_state_a.load() + with open(tmp_dir / "dir/subdir/d.txt", "w", encoding="utf-8") as dst: + dst.write("d") + dir_state_b = director["/qdp/source/b"] + dir_state_b.load() + dir_state_e = director["/qdp/source/e"] + dir_state_e.load() + variant["enabled"] = ["archive"] + director.build_package(None, None) + log = get_and_clear_log(caplog) + assert "/qdp/steps/archive: duplicate files in directory states /qdp/source/a and /qdp/source/b" in log + assert f"/qdp/steps/archive: duplicate file: {tmp_dir}/dir/subdir/d.txt" in log + assert f"/qdp/steps/archive: inconsistent file hashes for '{tmp_dir}/dir/subdir/d.txt': {list(dir_state_a.files_and_hashes())[2][1]} != {list(dir_state_b.files_and_hashes())[2][1]}" in log + assert f"/qdp/steps/archive: duplicate file: {tmp_dir}/dir/subdir/c.txt" in log + with tarfile.open(director["/qdp/deployment/archive"].file, + "r:*") as archive: + assert archive.getnames() == [ + 'dir/a.txt', 'dir/subdir/c.txt', 'dir/subdir/d.txt', 'dir/b.txt', + 'dir/subdir/c.txt', 'dir/subdir/d.txt', 'dir/e.txt', + 'verify_package.py' + ] + + verify_package = director["/qdp/deployment/verify-package"] + stdout = [] + status = run_command([verify_package.file, "--list-files-and-hashes"], + str(tmp_dir), stdout) + assert status == 0 + assert stdout == [ + "dir/a.txt\t7a296fab5364b34ce3e0476d55bf291bd41aa085e5ecf2a96883e593aa1836fed22f7242af48d54af18f55c8d1def13ec9314c926666a0ba63f7663500090565", + "dir/b.txt\t480a2ddd53e8db95fc737b670302c7ea0914b52ffdb2e961c2ff90887ec2b25873723374da81ae5adafc47ef7ef1c7c5c91243217d41cb904040279b758da0f7", + "dir/e.txt\t61e9f9edbc37b2b5c2fc9633da2d8777916f0e4515a080374acedd14c935f2c6fb5a882c5459b7a06a03f0d057ce4f73f89def713a5824b8769a5917a3bdda93", + "dir/subdir/c.txt\t663049a20dfea6b8da28b2eb90eddd10ccf28ef2519563310b9bde25b7268444014c48c4384ee5c5a54e7830e45fcd87df7910a7fda77b68c2efdd75f8de25e8", + "dir/subdir/d.txt\t48fb10b15f3d44a09dc82d02b06581e0c0c69478c9fd2cf8f9093659019a1687baecdbb38c9e72b12169dc4148690f87467f9154f5931c5df665c6496cbfd5f5" + ] diff --git a/spec-qdp/spec/qdp-archive.yml b/spec-qdp/spec/qdp-archive.yml new file mode 100644 index 00000000..e4292d10 --- /dev/null +++ b/spec-qdp/spec/qdp-archive.yml @@ -0,0 +1,58 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2020, 2021 embedded brains GmbH & Co. KG +enabled-by: true +links: +- role: spec-member + uid: root +- role: spec-refinement + spec-key: build-step-type + spec-value: archive + uid: qdp-build-step +spec-description: | + Items of this type shall have the following links: + + * There shall be links to a ${qdp-directory-state:/spec-name} item + with the ${qdp-input-role:/spec-name} and the name + ``"member"``. The link target directory state item defines the member + files of the archive. + + * There shall be exactly one link to a ${qdp-directory-state:/spec-name} item + with the ${qdp-output-role:/spec-name} and the name + ``"archive"``. The link target directory state item defines the + destination directory of the archive file. + + * There shall be exactly one link to a ${qdp-directory-state:/spec-name} item + with the ${qdp-output-role:/spec-name} and the name + ``"verify-package"``. The link target directory state item defines the + destination directory of the package verification script. +spec-example: null +spec-info: + dict: + attributes: + archive-file: + description: | + It shall be the path to the archive file relative to the base + directory of the ``"archive"`` directory state production. The + method to compress the archive is determined by the archive file name + extension. + spec-type: str + archive-strip-prefix: + description: | + It shall be the prefix to strip from an archive member file path. + spec-type: str + verification-script: + description: | + It shall be the verification script file name. The verification + script is automatically generated and will be included in the + archive. The script can be used to verify that a file of an unpacked + archive has the same state as the file packed into the archive. It + shows also missing and additional files inside an unpacked archive + base directory. + spec-type: str + description: | + This set of attributes specifies the package archive file and content. + mandatory-attributes: all +spec-name: Archive Item Type +spec-type: qdp-archive +type: spec -- cgit v1.2.3