mirror of
https://github.com/anchore/syft.git
synced 2026-11-01 15:06:36 +01:00
Add release process (#89)
* add check for app update; fix ETUI error handling * validate user args * add goreleaser support * replace cgo dependencies (go-rpm) with go equivalents * add acceptance tests against build snapshot * add brew tap + acceptance test pipeline * add mac acceptance tests * fix compare makefile * fix mac acceptance tests * add release pipeline with wait checks * add token to release step * rm dir presenters int test * enforce dpkg to be non interactive Co-authored-by: Alfredo Deza <adeza@anchore.com> * pin brew formulae * pin skopeo to formulae url * only run acceptance tests Co-authored-by: Alfredo Deza <adeza@anchore.com>
This commit is contained in:
co-authored by
Alfredo Deza
parent
3cb7c43dbc
commit
ba4f63099d
Executable
+109
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import json
|
||||
import collections
|
||||
|
||||
Metadata = collections.namedtuple("Metadata", "metadata sources")
|
||||
Package = collections.namedtuple("Package", "name type version")
|
||||
Vulnerability = collections.namedtuple("Vulnerability", "cve package")
|
||||
|
||||
|
||||
class ImgBom:
|
||||
def __init__(self, report_path):
|
||||
self.report_path = report_path
|
||||
|
||||
def _enumerate_section(self, section):
|
||||
with open(self.report_path) as json_file:
|
||||
data = json.load(json_file)
|
||||
for entry in data[section]:
|
||||
yield entry
|
||||
|
||||
def packages(self):
|
||||
packages = set()
|
||||
metadata = collections.defaultdict(dict)
|
||||
for entry in self._enumerate_section(section="artifacts"):
|
||||
package = Package(
|
||||
name=entry["name"], type=entry["type"], version=entry["version"]
|
||||
)
|
||||
|
||||
packages.add(package)
|
||||
metadata[package.type][package] = Metadata(
|
||||
metadata=repr(entry["metadata"]), sources=repr(entry["sources"])
|
||||
)
|
||||
return packages, metadata
|
||||
|
||||
|
||||
def main(baseline_report, new_report):
|
||||
report1_obj = ImgBom(report_path=baseline_report)
|
||||
report1_packages, report1_metadata = report1_obj.packages()
|
||||
|
||||
report2_obj = ImgBom(report_path=new_report)
|
||||
report2_packages, report2_metadata = report2_obj.packages()
|
||||
|
||||
if len(report2_packages) == 0 and len(report1_packages) == 0:
|
||||
print("nobody found any packages")
|
||||
return 0
|
||||
|
||||
same_packages = report2_packages & report1_packages
|
||||
percent_overlap_packages = (
|
||||
float(len(same_packages)) / float(len(report1_packages))
|
||||
) * 100.0
|
||||
|
||||
extra_packages = report2_packages - report1_packages
|
||||
missing_pacakges = report1_packages - report2_packages
|
||||
|
||||
report1_metadata_set = set()
|
||||
for package in report1_packages:
|
||||
metadata = report1_metadata[package.type][package]
|
||||
report1_metadata_set.add((package, metadata))
|
||||
|
||||
report2_metadata_set = set()
|
||||
for package in report2_packages:
|
||||
metadata = report2_metadata[package.type][package]
|
||||
report2_metadata_set.add((package, metadata))
|
||||
|
||||
same_metadata = report2_metadata_set & report1_metadata_set
|
||||
percent_overlap_metadata = 0
|
||||
if len(report1_metadata_set) > 0:
|
||||
percent_overlap_metadata = (
|
||||
float(len(same_metadata)) / float(len(report1_metadata_set))
|
||||
) * 100.0
|
||||
|
||||
if len(extra_packages) > 0:
|
||||
print("Extra packages:")
|
||||
for package in sorted(list(extra_packages)):
|
||||
print(" " + repr(package))
|
||||
print()
|
||||
|
||||
if len(missing_pacakges) > 0:
|
||||
print("Missing packages:")
|
||||
for package in sorted(list(missing_pacakges)):
|
||||
print(" " + repr(package))
|
||||
print()
|
||||
|
||||
print("Baseline Packages: %d" % len(report1_packages))
|
||||
print("New Packages: %d" % len(report2_packages))
|
||||
print()
|
||||
print(
|
||||
"Baseline Packages Matched: %.2f %% (%d/%d packages)"
|
||||
% (percent_overlap_packages, len(same_packages), len(report1_packages))
|
||||
)
|
||||
print(
|
||||
"Baseline Metadata Matched: %.2f %% (%d/%d metadata)"
|
||||
% (percent_overlap_metadata, len(same_metadata), len(report1_metadata_set))
|
||||
)
|
||||
|
||||
if len(report1_packages) != len(report2_packages):
|
||||
print("failed quality gate: requires exact name & version match")
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\nComparing two imgbom reports...\n")
|
||||
if len(sys.argv) != 3:
|
||||
sys.exit("please provide two imgbom json files")
|
||||
|
||||
rc = main(sys.argv[1], sys.argv[2])
|
||||
sys.exit(rc)
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
BOLD="$(tput -T linux bold)"
|
||||
RED="$(tput -T linux setaf 1)"
|
||||
RESET="$(tput -T linux sgr0)"
|
||||
FAIL="${BOLD}${RED}"
|
||||
SUCCESS="${BOLD}"
|
||||
JQ_ARGS="-S .artifacts"
|
||||
|
||||
if ! command -v jq &> /dev/null ;then
|
||||
JQ_IMAGE="imega/jq:latest"
|
||||
JQ_CMD="docker run --rm -i ${JQ_IMAGE} ${JQ_ARGS}"
|
||||
docker pull "${JQ_IMAGE}"
|
||||
else
|
||||
JQ_CMD="jq ${JQ_ARGS}"
|
||||
fi
|
||||
|
||||
if [[ $(cat $1 | ${JQ_CMD}) ]]; then
|
||||
set -x
|
||||
# compare the output of both results
|
||||
diff <(cat $1 | ${JQ_CMD}) <(cat $2 | ${JQ_CMD})
|
||||
set +x
|
||||
echo "${SUCCESS}Comparison passed!${RESET}"
|
||||
else
|
||||
exit "${FAIL}Failing since one of the test files is empty ($1)${RESET}"
|
||||
fi
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eux
|
||||
|
||||
DISTDIR=$1
|
||||
ACC_DIR=$2
|
||||
TEST_IMAGE=$3
|
||||
|
||||
TEST_TYPE=deb
|
||||
WORK_DIR=`mktemp -d -t "imgbom-acceptance-test-${TEST_TYPE}-XXXXXX"`
|
||||
REPORT=${WORK_DIR}/acceptance-${TEST_TYPE}-${TEST_IMAGE}.json
|
||||
GOLDEN_REPORT=${ACC_DIR}/test-fixtures/acceptance-${TEST_IMAGE}.json
|
||||
|
||||
# check if tmp dir was created
|
||||
if [[ ! "${WORK_DIR}" || ! -d "${WORK_DIR}" ]]; then
|
||||
echo "Could not create temp dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function cleanup {
|
||||
rm -rf "${WORK_DIR}"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# fetch test image
|
||||
docker pull ${TEST_IMAGE}
|
||||
|
||||
# install and run imgbom
|
||||
docker run --rm \
|
||||
-v /var/run/docker.sock://var/run/docker.sock \
|
||||
-v /${PWD}:/src \
|
||||
-v ${WORK_DIR}:${WORK_DIR} \
|
||||
-w /src \
|
||||
ubuntu:latest \
|
||||
/bin/bash -x -c "\
|
||||
DEBIAN_FRONTEND=noninteractive apt install ${DISTDIR}/imgbom_*_linux_amd64.deb -y && \
|
||||
imgbom version -v && \
|
||||
imgbom ${TEST_IMAGE} -vv -o json > ${REPORT} && \
|
||||
cat ${REPORT} \
|
||||
"
|
||||
|
||||
# compare the results to a known good output
|
||||
${ACC_DIR}/compare.py \
|
||||
${GOLDEN_REPORT} \
|
||||
${REPORT}
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eux
|
||||
|
||||
DISTDIR=$1
|
||||
ACC_DIR=$2
|
||||
TEST_IMAGE=$3
|
||||
|
||||
TEST_IMAGE_TAR=/tmp/image.tar
|
||||
TEST_TYPE=mac
|
||||
WORK_DIR=`mktemp -d -t "imgbom-acceptance-test-${TEST_TYPE}-XXXXXX"`
|
||||
REPORT=${WORK_DIR}/acceptance-${TEST_TYPE}-${TEST_IMAGE}.json
|
||||
GOLDEN_REPORT=${ACC_DIR}/test-fixtures/acceptance-${TEST_IMAGE}.json
|
||||
|
||||
# check if tmp dir was created
|
||||
if [[ ! "${WORK_DIR}" || ! -d "${WORK_DIR}" ]]; then
|
||||
echo "Could not create temp dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function cleanup {
|
||||
rm -rf "${WORK_DIR}"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# install skopeo (pinned to 1.1.0)
|
||||
skopeo --version || brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/75e8d7a40af77b48cc91f4bdb7d669f891a6de60/Formula/skopeo.rb
|
||||
|
||||
# fetch test image
|
||||
skopeo --override-os linux copy docker://docker.io/${TEST_IMAGE} docker-archive:${TEST_IMAGE_TAR}
|
||||
ls -alh ${TEST_IMAGE_TAR}
|
||||
|
||||
# run imgbom
|
||||
chmod 755 ${DISTDIR}/imgbom_darwin_amd64/imgbom
|
||||
${DISTDIR}/imgbom_darwin_amd64/imgbom version -v
|
||||
${DISTDIR}/imgbom_darwin_amd64/imgbom docker-archive://${TEST_IMAGE_TAR} -vv -o json > ${REPORT}
|
||||
cat ${REPORT}
|
||||
|
||||
# compare the results to a known good output
|
||||
${ACC_DIR}/compare.py \
|
||||
${GOLDEN_REPORT} \
|
||||
${REPORT}
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eux
|
||||
|
||||
DISTDIR=$1
|
||||
ACC_DIR=$2
|
||||
TEST_IMAGE=$3
|
||||
|
||||
TEST_TYPE=rpm
|
||||
WORK_DIR=`mktemp -d -t "imgbom-acceptance-test-${TEST_TYPE}-XXXXXX"`
|
||||
REPORT=${WORK_DIR}/acceptance-${TEST_TYPE}-${TEST_IMAGE}.json
|
||||
GOLDEN_REPORT=${ACC_DIR}/test-fixtures/acceptance-${TEST_IMAGE}.json
|
||||
|
||||
# check if tmp dir was created
|
||||
if [[ ! "${WORK_DIR}" || ! -d "${WORK_DIR}" ]]; then
|
||||
echo "Could not create temp dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function cleanup {
|
||||
rm -rf "${WORK_DIR}"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# fetch test image
|
||||
docker pull ${TEST_IMAGE}
|
||||
|
||||
# install and run imgbom
|
||||
docker run --rm \
|
||||
-v /var/run/docker.sock://var/run/docker.sock \
|
||||
-v /${PWD}:/src \
|
||||
-v ${WORK_DIR}:${WORK_DIR} \
|
||||
-w /src \
|
||||
centos:latest \
|
||||
/bin/bash -x -c "\
|
||||
rpm -ivh ${DISTDIR}/imgbom_*_linux_amd64.rpm && \
|
||||
imgbom version -v && \
|
||||
imgbom ${TEST_IMAGE} -vv -o json > ${REPORT} && \
|
||||
cat ${REPORT} \
|
||||
"
|
||||
|
||||
# compare the results to a known good output
|
||||
${ACC_DIR}/compare.py \
|
||||
${GOLDEN_REPORT} \
|
||||
${REPORT}
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user