Remove lots of legacy 'challenge' code (#1162)

Preparation for #1122.

* `pmb.parse.apkindex.parse()`, removed strict parameter: This used to raise
  an exception when two entries in the apkindex provided the same package.
  Turns out this is *not* invalid after all, two packages can provide the same
  soname for example (e.g. libhybris, mesa-egl). In an APKINDEX, sonames are
  listed as they were packages ("so:libjpeg.so.8" etc.).
* Remove `pmbootstrap challenge` leftover code from reproducible builds effort,
  which was a dead end. This code uses the broken strict feature.
This commit is contained in:
Oliver Smith 2018-01-25 18:08:39 +00:00 committed by GitHub
parent 7f9fc68729
commit 0479031f7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 4 additions and 931 deletions

View File

@ -1,23 +0,0 @@
"""
Copyright 2018 Oliver Smith
This file is part of pmbootstrap.
pmbootstrap is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pmbootstrap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
# Exported functions
from pmb.challenge.apk_file import apk
from pmb.challenge.apkindex import apkindex
from pmb.challenge.build import build
from pmb.challenge.frontend import frontend

View File

@ -1,124 +0,0 @@
"""
Copyright 2018 Oliver Smith
This file is part of pmbootstrap.
pmbootstrap is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pmbootstrap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import logging
import os
import tarfile
import tempfile
import filecmp
import shutil
def contents_diff(tar_a, tar_b, member_a, member_b, name):
# Extract both files
tars = [tar_a, tar_b]
members = [member_a, member_b]
temp_files = []
for i in range(2):
handle, path = tempfile.mkstemp("pmbootstrap")
handle = open(handle, "wb")
shutil.copyfileobj(tars[i].extractfile(members[i]), handle)
handle.close()
temp_files.append(path)
# Compare and delete
equal = filecmp.cmp(temp_files[0], temp_files[1], shallow=False)
for temp_file in temp_files:
os.remove(temp_file)
if equal:
logging.verbose("=> OK!")
else:
raise RuntimeError("File '" + name + "' is different!")
def contents_without_signature(tar, tar_name):
"""
The signature file name is always different.
This function raises an exception, when the number of signature
files in the archive is not 1.
:returns: a sorted list of all filenames inside the tar archive,
except for the signature file.
"""
names = tar.getnames()
found = False
ret = []
for name in names:
if name.startswith(".SIGN.RSA."):
if found:
raise RuntimeError("More than one signature file found"
" inside " + tar_name + ": " +
str(names))
else:
found = True
else:
ret.append(name)
if not found:
raise RuntimeError("No signature file found inside " +
tar_name + ": " + str(names))
return sorted(ret)
def apk(args, apk_a, apk_b):
with tarfile.open(apk_a, "r:gz") as tar_a:
with tarfile.open(apk_b, "r:gz") as tar_b:
# List of files must be the same
list_a = contents_without_signature(tar_a, apk_a)
list_b = contents_without_signature(tar_b, apk_b)
if list_a != list_b:
logging.info("Files in " + apk_a + ":" + str(list_a))
logging.info("Files in " + apk_b + ":" + str(list_b))
raise RuntimeError(
"Both APKs do not contain the same file names!")
# Iterate through the list
success = True
for name in list_a:
try:
logging.verbose("Compare: " + name)
if name == ".PKGINFO":
logging.verbose(
"=> Skipping: expected to be different")
continue
# Get members
member_a = tar_a.getmember(name)
member_b = tar_b.getmember(name)
if member_a.type != member_b.type:
raise RuntimeError(
"Entry '" + name + "' has a different type!")
if member_a.isdir():
logging.verbose("=> Skipping: directory")
elif member_a.isfile():
contents_diff(tar_a, tar_b, member_a, member_b, name)
elif member_a.issym() or member_a.islnk():
if member_a.linkname == member_b.linkname:
logging.verbose(
"=> Both link to " + member_a.linkname)
else:
raise RuntimeError(
"Link " + name + " has a different target!")
else:
raise RuntimeError(
"Can't diff '" + name + "', unsupported type!")
except Exception as e:
logging.info("CHALLENGE FAILED for " + name + ":" + str(e))
success = False
if not success:
raise RuntimeError("Challenge failed (see errors above)")

View File

@ -1,130 +0,0 @@
"""
Copyright 2018 Oliver Smith
This file is part of pmbootstrap.
pmbootstrap is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pmbootstrap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import logging
import os
import tarfile
import tempfile
import filecmp
import shutil
def contents_diff(tar_a, tar_b, member_a, member_b, name):
# Extract both files
tars = [tar_a, tar_b]
members = [member_a, member_b]
temp_files = []
for i in range(2):
handle, path = tempfile.mkstemp("pmbootstrap")
handle = open(handle, "wb")
shutil.copyfileobj(tars[i].extractfile(members[i]), handle)
handle.close()
temp_files.append(path)
# Compare and delete
equal = filecmp.cmp(temp_files[0], temp_files[1], shallow=False)
for temp_file in temp_files:
os.remove(temp_file)
if equal:
logging.debug("=> OK!")
else:
raise RuntimeError("File '" + name + "' is different!")
def contents_without_signature(tar, tar_name):
"""
The signature file name is always different.
This function raises an exception, when the number of signature
files in the archive is not 1.
:returns: a sorted list of all filenames inside the tar archive,
except for the signature file.
"""
names = tar.getnames()
found = False
ret = []
for name in names:
if name.startswith(".SIGN.RSA."):
if found:
raise RuntimeError("More than one signature file found"
" inside " + tar_name + ": " +
str(names))
else:
found = True
else:
ret.append(name)
if not found:
raise RuntimeError("No signature file found inside " +
tar_name + ": " + str(names))
return sorted(ret)
def apk(args, apk_a, apk_b, stop_after_first_error=False):
with tarfile.open(apk_a, "r:gz") as tar_a:
with tarfile.open(apk_b, "r:gz") as tar_b:
# List of files must be the same
list_a = contents_without_signature(tar_a, apk_a)
list_b = contents_without_signature(tar_b, apk_b)
if list_a != list_b:
logging.info("Files in " + apk_a + ":" + str(list_a))
logging.info("Files in " + apk_b + ":" + str(list_b))
raise RuntimeError(
"Both APKs do not contain the same file names!")
# Iterate through the list
success = True
for name in list_a:
try:
logging.debug("Compare: " + name)
if name == ".PKGINFO":
logging.debug(
"=> Skipping: expected to be different")
continue
# Get members
member_a = tar_a.getmember(name)
member_b = tar_b.getmember(name)
if member_a.type != member_b.type:
logging.info("NOTE: " + name + " in " + apk_a + ":")
tar_a.list(members=[member_a])
logging.info("NOTE: " + name + " in " + apk_b + ":")
tar_b.list(members=[member_b])
raise RuntimeError(
"Entry '" + name + "' has a different type!")
if member_a.isdir():
logging.debug("=> Skipping: directory")
elif member_a.isfile():
contents_diff(tar_a, tar_b, member_a, member_b, name)
elif member_a.issym() or member_a.islnk():
if member_a.linkname == member_b.linkname:
logging.debug(
"=> Both link to " + member_a.linkname)
else:
raise RuntimeError(
"Link " + name + " has a different target!")
else:
raise RuntimeError(
"Can't diff '" + name + "', unsupported type!")
except Exception as e:
logging.info("CHALLENGE FAILED for " + name + ":" + str(e))
success = False
if stop_after_first_error:
raise
if not success:
raise RuntimeError("Challenge failed (see errors above)")

View File

@ -1,68 +0,0 @@
"""
Copyright 2018 Oliver Smith
This file is part of pmbootstrap.
pmbootstrap is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pmbootstrap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import glob
import logging
import pmb.parse.apkindex
def apkindex(args, path_apkindex, apk_suffix=""):
"""
Verify an APKINDEX.tar.gz file, and its repository folder:
- Each apk must be defined inside the APKINDEX (once!)
- There must be no extra files
:param path_apkindex: full path to the APKINDEX.tar.gz
:param apk_suffix: set this to ".unverified", if all apk files in
the repository have such a suffix appended.
"""
# Parse the apkindex file
content = pmb.parse.apkindex.parse(args, path_apkindex, True)
folder = os.path.dirname(path_apkindex)
# All listed packages must exist
found = []
count = str(len(content.items()))
logging.info("Check for existence of all listed packages (" + count + ")")
for pkgname_alias, block in content.items():
apk = (block["pkgname"] + "-" + block["version"] + ".apk" +
apk_suffix)
if not os.path.exists(folder + "/" + apk):
raise RuntimeError("Could not find file '" + apk +
"' mentioned in " + path_apkindex)
# Mark the apk and its buildinfo (if it exists) as found
if apk not in found:
found.append(apk)
buildinfo = (block["pkgname"] + "-" + block["version"] +
".apk.buildinfo.json")
if os.path.exists(folder + "/" + buildinfo):
found.append(buildinfo)
# Add diff files, if they exist
for name in [apk + ".diff.md", buildinfo + ".diff.md"]:
if os.path.exists(folder + "/" + name):
found.append(name)
# There must be no extra files
logging.info("Check for extra files")
for path in glob.glob(folder + "/*"):
name = os.path.basename(path)
if name == "APKINDEX.tar.gz" or name in found:
continue
raise RuntimeError("Unexpected file '" + name + "' inside the"
" repository folder: " + folder)

View File

@ -1,78 +0,0 @@
"""
Copyright 2018 Oliver Smith
This file is part of pmbootstrap.
pmbootstrap is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pmbootstrap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import logging
import json
import os
import pmb.build
import pmb.parse.apkbuild
import pmb.helpers.repo
import pmb.challenge
def build(args, apk_path):
# Parse buildinfo
buildinfo_path = apk_path + ".buildinfo.json"
if not os.path.exists(buildinfo_path):
logging.info("NOTE: To create a .buildinfo.json file, use the"
" --buildinfo command while building: 'pmbootstrap build"
" --buildinfo <pkgname>'")
raise RuntimeError("Missing file: " + buildinfo_path)
with open(buildinfo_path) as handle:
buildinfo = json.load(handle)
# Install all listed packages
pmb.chroot.apk.install(args, buildinfo["versions"].keys())
# Verify the installed versions
installed = pmb.chroot.apk.installed(args)
for pkgname, version in buildinfo["versions"].items():
version_installed = installed[pkgname]["version"]
if version_installed != version:
raise RuntimeError("Dependency " + pkgname + " version is different"
" (installed: " + version_installed + ","
" buildinfo: " + version + ")!")
# Build the package
repo_before = pmb.helpers.repo.files(args)
pmb.build.package(args, buildinfo["pkgname"], buildinfo["arch"],
force=True, buildinfo=True)
repo_diff = pmb.helpers.repo.diff(args, repo_before)
# Diff the apk contents
staging_path = os.path.realpath(os.path.dirname(apk_path) + "/../")
for file in repo_diff:
file_staging = staging_path + "/" + file
file_work = args.work + "/packages/" + file
if file.endswith(".apk"):
logging.info("Verify " + file)
pmb.challenge.apk(args, file_staging, file_work)
elif (file.endswith("/APKINDEX.tar.gz") or
file.endswith(".apk.buildinfo.json")):
# We only verify the apk file (see above). The APKINDEX can
# be verified separately.
continue
else:
raise RuntimeError("Unknown file type changed in the"
" package repository folder: " + file)
# Output the changed files from the repository
if args.output_repo_changes:
with open(args.output_repo_changes, "w") as handler:
for file in repo_diff:
handler.write(file + "\n")

View File

@ -1,33 +0,0 @@
"""
Copyright 2018 Oliver Smith
This file is part of pmbootstrap.
pmbootstrap is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pmbootstrap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import logging
import pmb.challenge
def frontend(args):
path = args.challenge_file
logging.info("Challenge " + path)
if path.endswith(".apk"):
pmb.challenge.build(args, path)
elif os.path.basename(path) == "APKINDEX.tar.gz":
pmb.challenge.apkindex(args, path)
else:
raise ValueError("It is only possible to challenge files ending"
" in .apk or files named APKINDEX.tar.gz.")

View File

@ -29,7 +29,6 @@ import pmb.aportgen
import pmb.build
import pmb.build.autodetect
import pmb.config
import pmb.challenge
import pmb.chroot
import pmb.chroot.initfs
import pmb.chroot.other
@ -153,10 +152,6 @@ def build_init(args):
pmb.build.init(args, suffix)
def challenge(args):
pmb.challenge.frontend(args)
def checksum(args):
for package in args.packages:
pmb.build.checksum(args, package)

View File

@ -107,15 +107,11 @@ def parse_next_block(args, path, lines, start):
return None
def parse_add_block(path, strict, ret, block, pkgname=None):
def parse_add_block(path, ret, block, pkgname=None):
"""
Add one block to the return dictionary of parse().
:param path: to the APKINDEX.tar.gz
:param strict: When set to True, only allow one entry per pkgname.
In case there are two, raise an exception.
When set to False, and there are multiple entries
for one pkgname, it uses the latest one.
:param ret: dictionary of all packages in the APKINDEX, that is
getting built right now. This function will extend it.
:param block: return value from parse_next_block().
@ -131,9 +127,6 @@ def parse_add_block(path, strict, ret, block, pkgname=None):
# Handle duplicate entries
if pkgname in ret:
if strict:
raise RuntimeError("Multiple blocks for " + pkgname +
" in " + path)
# Ignore the block, if the block we already have has a higher
# version
version_old = ret[pkgname]["version"]
@ -145,14 +138,10 @@ def parse_add_block(path, strict, ret, block, pkgname=None):
ret[pkgname] = block
def parse(args, path, strict=False):
def parse(args, path):
"""
Parse an APKINDEX.tar.gz file, and return its content as dictionary.
:param strict: When set to True, only allow one entry per pkgname.
In case there are two, raise an exception.
When set to False, and there are multiple entries
for one pkgname, it uses the latest one.
:returns: a dictionary with the following structure:
{ "postmarketos-mkinitfs":
{
@ -189,10 +178,10 @@ def parse(args, path, strict=False):
break
# Add the next package and all aliases
parse_add_block(path, strict, ret, block)
parse_add_block(path, ret, block)
if "provides" in block:
for alias in block["provides"]:
parse_add_block(path, strict, ret, block, alias)
parse_add_block(path, ret, block, alias)
# Update the cache
args.cache["apkindex"][path] = {"lastmod": lastmod, "ret": ret}

View File

@ -328,21 +328,6 @@ def arguments():
for action in [kconfig_check, apkbuild_parse]:
action.add_argument("packages", nargs="*")
# Action: challenge
challenge = sub.add_parser("challenge",
help="verify, that all files in an apk can be"
" reproduced from the same sources /"
" verify, that an APKINDEX.tar.gz properly"
" lists all apks in a repository folder")
challenge.add_argument("--output-repo-changes", dest="output_repo_changes",
help="pass the path to a file here, to store a list"
" of apk- and APKINDEX-files that have been"
" changed during the build", default=None)
challenge.add_argument("challenge_file",
help="the file to be verified. must end in"
" .apk, or must be named"
" APKINDEX.tar.gz.")
# Action: apkindex_parse
apkindex_parse = sub.add_parser("apkindex_parse")
apkindex_parse.add_argument("apkindex_path")

View File

@ -1,272 +0,0 @@
"""
Copyright 2018 Oliver Smith
This file is part of pmbootstrap.
pmbootstrap is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pmbootstrap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import sys
import pytest
import tarfile
# Import from parent directory
sys.path.append(os.path.realpath(
os.path.join(os.path.dirname(__file__) + "/..")))
import pmb.challenge.apk_file
import pmb.config
import pmb.chroot.other
import pmb.helpers.logging
@pytest.fixture
def args(request):
import pmb.parse
sys.argv = ["pmbootstrap.py", "chroot"]
args = pmb.parse.arguments()
args.log = args.work + "/log_testsuite.txt"
pmb.helpers.logging.init(args)
request.addfinalizer(args.logfd.close)
return args
def test_apk_challenge_contents_diff(args):
"""
Create two tar files, which contain a file with the same name.
The content of that file is different.
"""
# Tempfolder inside chroot for fake apk files
temp_path = pmb.chroot.other.tempfolder(
args, "/tmp/test_apk_challenge_contents_diff")
temp_path_outside = args.work + "/chroot_native" + temp_path
# First file
name = "testfile"
apk_a = temp_path_outside + "/a.apk"
pmb.chroot.user(args, ["cp", "/etc/inittab", temp_path + "/" + name])
pmb.chroot.user(args, ["tar", "-czf", "a.apk", name],
working_dir=temp_path)
# Second file
apk_b = temp_path_outside + "/b.apk"
pmb.chroot.user(args, ["cp", "/etc/motd", temp_path + "/" + name])
pmb.chroot.user(args, ["tar", "-czf", "b.apk", name],
working_dir=temp_path)
# Compare OK
with tarfile.open(apk_a, "r:gz") as tar_a:
member_a = tar_a.getmember(name)
pmb.challenge.apk_file.contents_diff(
tar_a, tar_a, member_a, member_a, name)
# Compare NOK
with tarfile.open(apk_b, "r:gz") as tar_b:
member_b = tar_b.getmember(name)
with pytest.raises(RuntimeError) as e:
pmb.challenge.apk_file.contents_diff(tar_a, tar_b, member_a,
member_b, name)
assert str(e.value).endswith(" is different!")
def test_apk_challenge_contents_without_signature(args):
# Tempfolder inside chroot for fake apk files
temp_path = pmb.chroot.other.tempfolder(
args, "/tmp/test_apk_challenge_nosig")
temp_path_outside = args.work + "/chroot_native" + temp_path
# Create three archives
contents = {
"no_sig.apk": ["other_file"],
"one_sig.apk": [".SIGN.RSA.first", "other_file"],
"two_sig.apk": [".SIGN.RSA.first", ".SIGN.RSA.second"],
}
for apk, files in contents.items():
for file in files:
pmb.chroot.user(args, ["touch", temp_path + "/" + file])
pmb.chroot.user(args, ["tar", "-czf", apk] +
files, working_dir=temp_path)
# No signature
with tarfile.open(temp_path_outside + "/no_sig.apk", "r:gz") as tar:
with pytest.raises(RuntimeError) as e:
pmb.challenge.apk_file.contents_without_signature(tar, "a.apk")
assert str(e.value).startswith("No signature file found")
# One signature
with tarfile.open(temp_path_outside + "/one_sig.apk", "r:gz") as tar:
contents = pmb.challenge.apk_file.contents_without_signature(
tar, "a.apk")
assert contents == ["other_file"]
# More than one signature
with tarfile.open(temp_path_outside + "/two_sig.apk", "r:gz") as tar:
with pytest.raises(RuntimeError) as e:
pmb.challenge.apk_file.contents_without_signature(tar, "a.apk")
assert str(e.value).startswith("More than one signature")
def test_apk_challenge_different_files_inside_archive(args):
# Tempfolder inside chroot for fake apk files
temp_path = pmb.chroot.other.tempfolder(args, "/tmp/test_apk_challenge")
temp_path_outside = args.work + "/chroot_native" + temp_path
# Create fake apks
contents = {
"a.apk": [".SIGN.RSA.first", "first_file", "second_file"],
"b.apk": [".SIGN.RSA.second", "first_file"],
}
for apk, files in contents.items():
for file in files:
pmb.chroot.user(args, ["touch", temp_path + "/" + file])
pmb.chroot.user(args, ["tar", "-czf", apk] +
files, working_dir=temp_path)
# Challenge both files
with pytest.raises(RuntimeError) as e:
pmb.challenge.apk(args, temp_path_outside + "/a.apk",
temp_path_outside + "/b.apk")
assert "do not contain the same file names" in str(e.value)
def test_apk_challenge_entry_has_a_different_type(args):
# Tempfolder inside chroot for fake apk files
temp_path = pmb.chroot.other.tempfolder(args, "/tmp/test_apk_challenge")
temp_path_outside = args.work + "/chroot_native" + temp_path
# Create fake apks
contents = {
"a.apk": [".SIGN.RSA.first", ".APKINDEX", "different_type"],
"b.apk": [".SIGN.RSA.second", ".APKINDEX", "different_type"],
}
for apk, files in contents.items():
for file in files:
if file == "different_type" and apk == "b.apk":
pmb.chroot.user(args, ["rm", temp_path + "/" + file])
pmb.chroot.user(args, ["mkdir", temp_path + "/" + file])
else:
pmb.chroot.user(args, ["touch", temp_path + "/" + file])
pmb.chroot.user(args, ["tar", "-czf", apk] +
files, working_dir=temp_path)
# Exact error (with stop_after_first_error)
with pytest.raises(RuntimeError) as e:
pmb.challenge.apk(args, temp_path_outside + "/a.apk",
temp_path_outside + "/b.apk", stop_after_first_error=True)
assert "has a different type!" in str(e.value)
# Generic error
with pytest.raises(RuntimeError) as e:
pmb.challenge.apk(args, temp_path_outside + "/a.apk",
temp_path_outside + "/b.apk")
assert "Challenge failed"
def test_apk_challenge_file_has_different_content(args):
# Tempfolder inside chroot for fake apk files
temp_path = pmb.chroot.other.tempfolder(args, "/tmp/test_apk_challenge")
temp_path_outside = args.work + "/chroot_native" + temp_path
# Create fake apks
contents = {
"a.apk": [".SIGN.RSA.first", ".APKINDEX", "different_content"],
"b.apk": [".SIGN.RSA.second", ".APKINDEX", "different_content"],
}
for apk, files in contents.items():
for file in files:
if file == "different_content" and apk == "b.apk":
pmb.chroot.user(
args, [
"cp", "/etc/hostname", temp_path + "/" + file])
else:
pmb.chroot.user(args, ["touch", temp_path + "/" + file])
pmb.chroot.user(args, ["tar", "-czf", apk] +
files, working_dir=temp_path)
# Exact error (with stop_after_first_error)
with pytest.raises(RuntimeError) as e:
pmb.challenge.apk(args, temp_path_outside + "/a.apk",
temp_path_outside + "/b.apk", stop_after_first_error=True)
assert str(e.value).endswith("is different!")
# Generic error
with pytest.raises(RuntimeError) as e:
pmb.challenge.apk(args, temp_path_outside + "/a.apk",
temp_path_outside + "/b.apk")
assert "Challenge failed"
def test_apk_challenge_different_link_target(args):
# Tempfolder inside chroot for fake apk files
temp_path = pmb.chroot.other.tempfolder(args, "/tmp/test_apk_challenge")
temp_path_outside = args.work + "/chroot_native" + temp_path
# Create fake apks
contents = {
"a.apk": [".SIGN.RSA.first", ".APKINDEX", "link_same", "link_different"],
"b.apk": [".SIGN.RSA.second", ".APKINDEX", "link_same", "link_different"],
}
for apk, files in contents.items():
for file in files:
if file.startswith("link_"):
if file == "link_different" and apk == "b.apk":
pmb.chroot.user(args, ["ln", "-sf", "/different_target",
temp_path + "/" + file])
else:
pmb.chroot.user(args, ["ln", "-sf", "/some_link_target",
temp_path + "/" + file])
else:
pmb.chroot.user(args, ["touch", temp_path + "/" + file])
pmb.chroot.user(args, ["tar", "-czf", apk] +
files, working_dir=temp_path)
# Exact error (with stop_after_first_error)
with pytest.raises(RuntimeError) as e:
pmb.challenge.apk(args, temp_path_outside + "/a.apk",
temp_path_outside + "/b.apk", stop_after_first_error=True)
assert str(e.value).endswith("has a different target!")
# Generic error
with pytest.raises(RuntimeError) as e:
pmb.challenge.apk(args, temp_path_outside + "/a.apk",
temp_path_outside + "/b.apk")
assert "Challenge failed"
def test_apk_challenge_unsupported_type(args):
# Tempfolder inside chroot for fake apk files
temp_path = pmb.chroot.other.tempfolder(args, "/tmp/test_apk_challenge")
temp_path_outside = args.work + "/chroot_native" + temp_path
# Create fake apk with a FIFO (-> unsupported type)
apk = "test.apk"
content = [".SIGN.RSA.first", ".APKINDEX", "fifo"]
for file in content:
if file == "fifo":
pmb.chroot.user(args, ["mkfifo", temp_path + "/" + file])
else:
pmb.chroot.user(args, ["touch", temp_path + "/" + file])
pmb.chroot.user(args, ["tar", "-czf", apk] +
content, working_dir=temp_path)
# Exact error (with stop_after_first_error)
with pytest.raises(RuntimeError) as e:
pmb.challenge.apk(args, temp_path_outside + "/test.apk",
temp_path_outside + "/test.apk", stop_after_first_error=True)
assert str(e.value).endswith("unsupported type!")
# Generic error
with pytest.raises(RuntimeError) as e:
pmb.challenge.apk(args, temp_path_outside + "/test.apk",
temp_path_outside + "/test.apk")
assert "Challenge failed"

View File

@ -1,89 +0,0 @@
"""
Copyright 2018 Oliver Smith
This file is part of pmbootstrap.
pmbootstrap is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pmbootstrap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import sys
import pytest
# Import from parent directory
sys.path.append(os.path.realpath(
os.path.join(os.path.dirname(__file__) + "/..")))
import pmb.challenge.apkindex
import pmb.config
import pmb.helpers.logging
@pytest.fixture
def args(request, tmpdir):
import pmb.parse
sys.argv = ["pmbootstrap.py", "chroot"]
args = pmb.parse.arguments()
args.log = args.work + "/log_testsuite.txt"
pmb.helpers.logging.init(args)
request.addfinalizer(args.logfd.close)
# Create an empty APKINDEX.tar.gz file, so we can use its path and
# timestamp to put test information in the cache.
path_apkindex = str(tmpdir) + "/APKINDEX.tar.gz"
open(path_apkindex, "a").close()
lastmod = os.path.getmtime(path_apkindex)
args.cache["apkindex"][path_apkindex] = {"lastmod": lastmod, "ret": {}}
return args
def test_challenge_apkindex_extra_file(args):
"""
Create an extra file, that is not mentioned in the APKINDEX cache.
"""
path_apkindex = list(args.cache["apkindex"].keys())[0]
tmpdir = os.path.dirname(path_apkindex)
open(tmpdir + "/invalid-extra-file.apk", "a").close()
with pytest.raises(RuntimeError) as e:
pmb.challenge.apkindex(args, path_apkindex)
assert "Unexpected file" in str(e.value)
def test_challenge_apkindex_file_does_not_exist(args):
"""
Add an entry to the APKINDEX cache, that does not exist on disk.
"""
path_apkindex = list(args.cache["apkindex"].keys())[0]
args.cache["apkindex"][path_apkindex]["ret"] = {
"hello-world": {"pkgname": "hello-world", "version": "1-r2"}
}
with pytest.raises(RuntimeError) as e:
pmb.challenge.apkindex(args, path_apkindex)
assert str(e.value).startswith("Could not find file 'hello-world")
def test_challenge_apkindex_ok(args):
"""
Metion one file in the APKINDEX cache, and create it on disk. The challenge
should go through without an exception.
"""
path_apkindex = list(args.cache["apkindex"].keys())[0]
args.cache["apkindex"][path_apkindex]["ret"] = {
"hello-world": {"pkgname": "hello-world", "version": "1-r2"}
}
tmpdir = os.path.dirname(path_apkindex)
open(tmpdir + "/hello-world-1-r2.apk", "a").close()
pmb.challenge.apkindex(args, path_apkindex)

View File

@ -1,79 +0,0 @@
"""
Copyright 2018 Oliver Smith
This file is part of pmbootstrap.
pmbootstrap is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pmbootstrap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import sys
import pytest
# Import from parent directory
sys.path.append(os.path.realpath(
os.path.join(os.path.dirname(__file__) + "/..")))
import pmb.build
import pmb.challenge.build
import pmb.config
import pmb.helpers.logging
import pmb.parse
@pytest.fixture
def args(request, tmpdir):
import pmb.parse
sys.argv = ["pmbootstrap.py", "chroot"]
args = pmb.parse.arguments()
args.log = args.work + "/log_testsuite.txt"
pmb.helpers.logging.init(args)
request.addfinalizer(args.logfd.close)
return args
def test_challenge_build(args):
# Build the "hello-world" package
pkgname = "hello-world"
pmb.build.package(args, pkgname, None, force=True, buildinfo=True)
# Copy it to a temporary path
aport = pmb.build.other.find_aport(args, "hello-world")
apkbuild = pmb.parse.apkbuild(args, aport + "/APKBUILD")
version = apkbuild["pkgver"] + "-r" + apkbuild["pkgrel"]
temp_path = pmb.chroot.other.tempfolder(args, "/tmp/test_challenge_build/" +
args.arch_native)
packages_path = "/home/pmos/packages/pmos/" + args.arch_native
apk_path = packages_path + "/" + pkgname + "-" + version + ".apk"
pmb.chroot.user(args, ["cp", apk_path, apk_path + ".buildinfo.json",
temp_path])
# Change the timestamps of all files, so the changes file gets written
# correctly, even if this testcase gets executed very fast
pmb.chroot.user(args, ["touch", "-d", "2017-01-01",
packages_path + "/APKINDEX.tar.gz",
apk_path,
apk_path + ".buildinfo.json"])
# Challenge, output changes into a file
args.cache["built"] = {}
setattr(args, "output_repo_changes", args.work + "/chroot_native/tmp/"
"test_challenge_build_output.txt")
pmb.challenge.build(args, args.work + "/chroot_native/" + temp_path + "/" +
os.path.basename(apk_path))
# Verify the output textfile
with open(args.output_repo_changes, "r") as handle:
lines = handle.readlines()
assert lines == [args.arch_native + "/APKINDEX.tar.gz\n",
args.arch_native + "/" + pkgname + "-" + version + ".apk\n",
args.arch_native + "/" + pkgname + "-" + version + ".apk.buildinfo.json\n"]