#!/bin/bash # This script requires an OCI IMAGE Name to pull. # The script generates a SOURCE Image based on the OCI Image # Script must be executed on the same OS or newer as the image. export ABV_NAME="SrcImg" # TODO maybe a flag for this? export source_image_suffix="-source" _usage() { echo "Usage: $(basename $0) [-b ] [-c ] [-e ] [-o ] [-p] IMAGE" echo "" echo -e " -b \tbase path for source image builds" echo -e " -c \tbuild context for the container image. Can be provided via CONTEXT_DIR env variable" echo -e " -e \textra src for the container image. Can be provided via EXTRA_SRC_DIR env variable" echo -e " -r \tdirectory of RPMS to add. Can be provided via RPM_DIR env variable" echo -e " -o \toutput the OCI image to path. Can be provided via OUTPUT_DIR env variable" echo -e " -d \toutput the OCI image to path. Can be provided via OUTPUT_DIR env variable" echo -e " -l\t\tlist the source drivers available" echo -e " -p\t\tpush source image after build" echo -e " -D\t\tdebuging output. Can be set via DEBUG env variable" exit 1 } # # sanity checks on startup # _init() { if [ $# -lt 1 ] ; then _usage fi set -o pipefail # check for tools we depend on for cmd in jq skopeo dnf file find ; do if [ -z "$(command -v ${cmd})" ] ; then # TODO: maybe this could be individual checks so it can report # where to find the tools echo "ERROR: please install '${cmd}' package" fi done } _is_sourced() { # https://unix.stackexchange.com/a/215279 # thanks @tianon [ "${FUNCNAME[${#FUNCNAME[@]} - 1]}" == 'source' ] } # count $character $string _count() { #expr $(echo "${2}" | tr "${1}" '\n' | wc -l) - 1 c="${2//[^${1}]}" echo -n ${#c} } # size of file in bytes _size() { local file="${1}" stat -c "%s" "${file}" | tr -d '\n' } # date timestamp in RFC 3339, to the nanosecond _date_ns() { date --rfc-3339=ns | tr -d '\n' } # local `mktemp -d` _mktemp_d() { mktemp -d "${TMPDIR:-/tmp}/${ABV_NAME}.XXXXXX" } # local `mktemp` _mktemp() { mktemp "${TMPDIR:-/tmp}/${ABV_NAME}.XXXXXX" } # local rm -rf _rm_rf() { _debug "rm -rf $@" #rm -rf $@ } # local tar _tar() { if [ -n "${DEBUG}" ] ; then tar -v $@ else tar $@ fi } # output things, only when $DEBUG is set _debug() { if [ -n "${DEBUG}" ] ; then echo "[${ABV_NAME}][DEBUG] ${@}" fi } # general echo but with prefix _info() { echo "[${ABV_NAME}][INFO] ${@}" } # general echo but with prefix _error() { echo "[${ABV_NAME}][ERROR] ${@}" >&2 exit 1 } # # parse the OCI image reference, accounting for: # * transport name # * presence or lack of transport port number # * presence or lack of digest # * presence or lack of image tag # # # return the image reference's digest, if any # parse_img_digest() { local ref="${1}" local digest="" if [ "$(_count '@' ${ref})" -gt 0 ] ; then digest="${ref##*@}" # the digest after the "@" fi echo -n "${digest}" } # # determine image base name (without tag or digest) # parse_img_base() { local ref="${1%@*}" # just the portion before the digest "@" local base="${ref}" default to the same if [ "$(_count ':' $(echo ${ref} | tr '/' '\n' | tail -1 ))" -gt 0 ] ; then # which means everything before it is the base image name, **including # transport (which could have a port delineation), and even a URI base="$(echo ${ref} | rev | cut -d : -f 2 | rev )" fi echo -n "${base}" } # # determine, or guess, the image tag from the provided image reference # parse_img_tag() { local ref="${1%@*}" # just the portion before the digest "@" local tag="latest" # default tag if [ "$(_count ':' $(echo ${ref} | tr '/' '\n' | tail -1 ))" -gt 0 ] ; then # if there are colons in the last segment after '/', then get that tag name tag="$(echo ${ref} | tr '/' '\n' | tail -1 | cut -d : -f 2 )" fi echo -n "${tag}" } # # an inline prefixer for containers/image tools # ref_prefix() { local ref="${1}" # get the supported prefixes of the current version of skopeo IFS=", " local pfxs=( $(skopeo copy --help | grep -A1 "Supported transports:" | grep -v "Supported transports") ) unset IFS for pfx in ${pfxs[@]} ; do if echo ${ref} | grep -q "^${pfx}:" ; then # break when we match echo ${ref} return 0 fi done # else default echo "docker://${ref}" } # # an inline namer for the source image # Initially this is a tagging convention (which if we try estesp/manifest-tool # can be directly mapped into a manifest-list/image-index). # ref_src_img_tag() { local ref="${1}" echo -n "$(parse_img_tag ${ref})${source_image_suffix}" } # # call out to registry for the image reference's digest checksum # fetch_img_digest() { local ref="${1}" ## TODO: check for authfile, creds, and whether it's an insecure registry local dgst=$(skopeo inspect "$(ref_prefix ${ref})" | jq .Digest | tr -d \") local ret=$? if [ $ret -ne 0 ] ; then echo "ERROR: check the image reference: ${ref}" >&2 return $ret fi echo -n "${dgst}" } # # pull down the image to an OCI layout # arguments: image ref # returns: path:tag to the OCI layout # # any commands should only output to stderr, so that the caller can receive the # path reference to the OCI layout. # fetch_img() { local ref="${1}" local dst="${2}" mkdir -p "${dst}" local base="$(parse_img_base ${ref})" local tag="$(parse_img_tag ${ref})" local dgst="$(parse_img_digest ${ref})" local from="" # skopeo currently only support _either_ tag _or_ digest, so we'll be specific. if [ -n "${dgst}" ] ; then from="$(ref_prefix ${base})@${dgst}" else from="$(ref_prefix ${base}):${tag}" fi ## TODO: check for authfile, creds, and whether it's an insecure registry ## destination name must have the image tag included (umoci expects it) skopeo \ copy \ "${from}" \ "oci:${dst}:${tag}" >&2 echo -n "${dst}:${tag}" } # # upack_img # unpack_img() { local image_dir="${1}" local unpack_dir="${2}" if [ -d "${unpack_dir}" ] ; then _rm_rf "${unpack_dir}" fi # TODO perhaps if uid == 0 and podman is present then we can try it? if [ -z "$(command -v umoci)" ] ; then # can be done as non-root (even in a non-root container) unpack_img_umoci "${image_dir}" "${unpack_dir}" else # can be done as non-root (even in a non-root container) unpack_img_bash "${image_dir}" "${unpack_dir}" fi } # # unpack an image layout using only jq and bash # unpack_img_bash() { local image_dir="${1}" local unpack_dir="${2}" local mnfst_dgst="$(cat "${image_dir}"/index.json | jq '.manifests[0].digest' | tr -d \" )" # Since we're landing the reference as an OCI layout, this mediaType is fairly predictable # TODO don't always assume +gzip layer_dgsts="$(cat ${image_dir}/blobs/${mnfst_dgst/:/\/} | \ jq '.layers[] | select(.mediaType == "application/vnd.oci.image.layer.v1.tar+gzip") | .digest' | tr -d \")" mkdir -vp "${unpack_dir}" for dgst in ${layer_dgsts} ; do path="${image_dir}/blobs/${dgst/:/\/}" tmp_file=$(_mktemp_d) zcat "${path}" | _tar -t > $tmp_file # TODO cleanup these files # look for '.wh.' entries. They must be removed from the rootfs # _before_ extracting the archive, then the .wh. entries themselves # need to not remain afterwards grep '\.wh\.' "${tmp_file}" | while read line ; do # if `some/path/.wh.foo` then `rm -rf `${unpack_dir}/some/path/foo` # if `some/path/.wh..wh..opq` then `rm -rf `${unpack_dir}/some/path/*` if [ "$(basename ${line})" == ".wh..wh..opq" ] ; then _rm_rf "${unpack_dir}/$(dirname ${line})/*" elif basename "${line}" | grep -qe '^\.wh\.' ; then name=$(basename "${line}" | sed -e 's/^\.wh\.//') _rm_rf "${unpack_dir}/$(dirname ${line})/${name}" fi done _info "[unpacking] layer ${dgst}" # unpack layer to rootfs (without whiteouts) zcat "${path}" | _tar --restrict --no-xattr --no-acls --no-selinux --exclude='*.wh.*' -x -C "${unpack_dir}" # some of the directories get unpacked as 0555, so removing them gives an EPERM find "${unpack_dir}" -type d -exec chmod 0755 "{}" \; done } # # unpack using umoci # unpack_img_umoci() { local image_dir="${1}" local unpack_dir="${2}" _debug "unpackging with umoci" # always assume we're not root I reckon umoci unpack --rootless --image "${image_dir}" "${unpack_dir}" >&2 } # TODO this is not worked out yet push_img() { local ref="${1}" local path="${2}" ## TODO: check for authfile, creds, and whether it's an insecure registry skopeo copy "oci:${path}:$(ref_src_img_tag ${ref})" "$(ref_prefix ${ref})" } # # sets up a basic new OCI layout, for an image with the provided (or default 'latest') tag # layout_new() { local out_dir="${1}" local image_tag="${2:-latest}" mkdir -p "${out_dir}/blobs/sha256" echo '{"imageLayoutVersion":"1.0.0"}' > "${out_dir}/oci-layout" local config=' { "created": "'$(_date_ns)'", "architecture": "amd64", "os": "linux", "config": {}, "rootfs": { "type": "layers", "diff_ids": [] } } ' local config_sum=$(echo "${config}" | jq -c | tr -d '\n' | sha256sum | awk '{ print $1 }' | tr -d '\n') echo "${config}" | jq -c | tr -d '\n' > "${out_dir}/blobs/sha256/${config_sum}" local mnfst=' { "schemaVersion": 2, "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "digest": "sha256:'"${config_sum}"'", "size": '"$(_size ${out_dir}/blobs/sha256/${config_sum})"' }, "layers": [] } ' local mnfst_sum=$(echo "${mnfst}" | jq -c | tr -d '\n' | sha256sum | awk '{ print $1 }' | tr -d '\n') echo "${mnfst}" | jq -c | tr -d '\n' > "${out_dir}/blobs/sha256/${mnfst_sum}" echo ' { "schemaVersion": 2, "manifests": [ { "mediaType": "application/vnd.oci.image.manifest.v1+json", "digest": "sha256:'"${mnfst_sum}"'", "size": '"$(_size ${out_dir}/blobs/sha256/${mnfst_sum})"', "annotations": { "org.opencontainers.image.ref.name": "'"${image_tag}"'" } } ] } ' | jq -c | tr -d '\n' > "${out_dir}/index.json" } # TODO this is not finished yet # call this for every artifact, to insert it into an OCI layout # args: # * a path to the layout # * a path to the artifact # * the path inside the tar # * tag used in the layout (default is 'latest') # layout_insert() { local out_dir="${1}" local artifact_path="${2}" local tar_path="${3}" local image_tag="${4:-latest}" local mnfst_list="${out_dir}/index.json" # get the digest to the manifest test -f "${mnfst_list}" || return 1 local mnfst_dgst="$(cat ${mnfst_list} | jq --arg tag "${image_tag}" ' .manifests[] | select(.annotations."org.opencontainers.image.ref.name" == $tag ) | .digest ' | tr -d \" | tr -d '\n' )" local mnfst="${out_dir}/blobs/${mnfst_dgst/:/\/}" test -f "${mnfst}" || return 1 # make tar of new object local tmpdir="$(_mktemp_d)" # TODO account for "artifact_path" being a directory? local sum="$(sha256sum ${artifact_path} | awk '{ print $1 }')" # making a blob store in the layer mkdir -p "${tmpdir}/blobs/sha256" cp "${artifact_path}" "${tmpdir}/blobs/sha256/${sum}" if [ "$(basename ${tar_path})" == "$(basename ${artifact_path})" ] ; then mkdir -p "${tmpdir}/$(dirname ${tar_path})" # TODO this symlink need to be relative path, not to `/blobs/...` ln -s "/blobs/sha256/${sum}" "${tmpdir}/${tar_path}" else mkdir -p "${tmpdir}/${tar_path}" # TODO this symlink need to be relative path, not to `/blobs/...` ln -s "/blobs/sha256/${sum}" "${tmpdir}/${tar_path}/$(basename ${artifact_path})" fi local tmptar="$(_mktemp)" # zero all the things for as consistent blobs as possible _tar -C "${tmpdir}" --mtime=@0 --owner=0 --group=0 --mode='a+rw' --no-xattrs --no-selinux --no-acls -cf "${tmptar}" . _rm_rf "${tmpdir}" # checksum tar and move to blobs/sha256/$checksum local tmptar_sum="$(sha256sum ${tmptar} | awk '{ print $1 }')" local tmptar_size="$(_size ${tmptar})" mv "${tmptar}" "${out_dir}/blobs/sha256/${tmptar_sum}" # find and read the prior config, mapped from the manifest local config_sum="$(jq '.config.digest' "${mnfst}" | tr -d \")" # use `jq` to append to prior config local tmpconfig="$(_mktemp)" cat "${out_dir}/blobs/${config_sum/:/\/}" | jq -c \ --arg date "$(_date_ns)" \ --arg tmptar_sum "sha256:${tmptar_sum}" \ --arg comment "#(nop) BuildSourceImage adding artifact: ${sum}" \ ' .created = $date | .rootfs.diff_ids = .rootfs.diff_ids + [ sha256:$tmptar_sum ] | .history = .history + [ { "created": $date, "created_by": $comment } ] ' > "${tmpconfig}" _rm_rf "${out_dir}/blobs/${config_sum/:/\/}" # rename the config blob to its new checksum local tmpconfig_sum="$(sha256sum ${tmpconfig} | awk '{ print $1 }')" local tmpconfig_size="$(_size ${tmpconfig})" mv "${tmpconfig}" "${out_dir}/blobs/sha256/${tmpconfig_sum}" # append layers list in the manifest, and its new config mapping local tmpmnfst="$(_mktemp)" cat "${mnfst}" | jq -c \ --arg tmpconfig_sum "sha256:${tmpconfig_sum}" \ --arg tmpconfig_size "${tmpconfig_size}" \ --arg tmptar_sum "sha256:${tmptar_sum}" \ --arg tmptar_size "${tmptar_size}" \ --arg artifact "$(basename ${artifact_path})" \ --arg sum "sha256:${sum}" \ ' .config.digest = sha256:$tmpconfig_sum | .config.size = $tmpconfig_size | .layers = .layers + [ { "mediaType": "application/vnd.oci.image.layer.v1.tar", "size": $tmptar_size, "digest": $tmptar_sum, "annotations": { "com.redhat.layer.type": "source", "com.redhat.layer.content": $artifact, "com.redhat.layer.content.checksum": $sum } } ] ' > "${tmpmnfst}" _rm_rf "${mnfst}" # rename the manifest blob to its new checksum local tmpmnfst_sum="$(sha256sum ${tmpmnfst} | awk '{ print $1 }')" local tmpmnfst_size="$(_size ${tmpmnfst})" mv "${tmpmnfst}" "${out_dir}/blobs/sha256/${tmpmnfst_sum}" # map the mnfst_list to the new mnfst checksum local tmpmnfst_list="$(_mktemp)" cat "${mnfst_list}" | jq -c \ --arg tag "${image_tag}" \ --arg tmpmnfst_sum "${tmpmnfst_sum}" \ --arg tmpmnfst_size "${tmpmnfst_size}" \ ' .manifests = [(.manifests[] | select(.annotations."org.opencontainers.image.ref.name" != "$tag") )] + [ { "mediaType": "application/vnd.oci.image.manifest.v1+json", "digest": "sha256:$tmpmnfst_sum", "size": $tmpmnfst_size, "annotations": { "com.redhat.image.type": "source" "org.opencontainers.image.ref.name": "$tag" } } ] ' > "${tmpmnfst_list}" mv "${tmpmnfst_list}" "${mnfst_list}" } # # Source Collection Drivers # # presently just bash functions. *notice* prefix the function name as `sourcedriver_` # May become a ${ABV_NAME}/drivers.d/ # # Arguments: # * image ref # * path to inspect # * output path for source (specifc to this driver) # * output path for source json metadata (this addresses the files to be added and it's metadata) # # TODO TBD this does not account for how to pack/layer the sources collected here. # This was my thought for outputing a `source.json` file, which is not a # standard, but could be an array of metadata about _each_ object that should # be packed. # # # driver to determine and fetch source rpms, based on the rootfs # sourcedriver_rpm_fetch() { local self="${0#sourcedriver_*}" local ref="${1}" local rootfs="${2}" local out_dir="${3}" local manifest_dir="${4}" # Get the RELEASEVER from the image local release=$(rpm -q --queryformat "%{VERSION}\n" --root ${rootfs} -f /etc/os-release) # From the rootfs of the works image, build out the src rpms to operate over for srcrpm in $(rpm -qa --root ${rootfs} --queryformat '%{SOURCERPM}\n' | grep -v '^gpg-pubkey' | sort -u) ; do local rpm=${srcrpm%*.src.rpm} if [ ! -f "${out_dir}/${srcrpm}" ] ; then _info "--> fetching ${srcrpm}" dnf download \ --quiet \ --installroot "${rootfs}" \ --release "${release}" \ --destdir "${out_dir}" \ --source \ "${rpm}" || continue else _info "--> using cached ${srcrpm}" fi # XXX one day, check and confirm with %{sourcepkgid} # https://bugzilla.redhat.com/show_bug.cgi?id=1741715 #local rpm_sourcepkgid=$(rpm -q --root ${rootfs} --queryformat '%{sourcepkgid}' "${rpm}") local srcrpm_buildtime=$( rpm -qp --qf '%{buildtime}' ${out_dir}/${srcrpm} ) local srcrpm_pkgid=$( rpm -qp --qf '%{pkgid}' ${out_dir}/${srcrpm} ) touch --date=@${srcrpm_buildtime} ${out_dir}/${srcrpm} local mimetype="$(file --brief --mime-type ${out_dir}/${srcrpm})" local source_info="${manifest_dir}/${srcrpm}.json" jq \ -n \ --arg name "${srcrpm}" \ --arg buildtime "${srcrpm_buildtime}" \ --arg mimetype "${mimetype}" \ ' { "source.artifact.name": $name, "source.artifact.mimetype": $mimetype, "source.artifact.buildtime": $buildtime } ' \ > "${source_info}" ret=$? if [ $ret -ne 0 ] ; then return 1 fi done } # # driver to only package rpms from a provided rpm directory # sourcedriver_rpm_dir() { local self="${0#sourcedriver_*}" local ref="${1}" local rootfs="${2}" local out_dir="${3}" local manifest_dir="${4}" if [ -n "${RPM_DIR}" ]; then _debug "$self: writing to $out_dir and $manifest_dir" fi } # # If the caller specified a context directory, # # slightly special driver, as it has a flag/env passed in, that it uses # sourcedriver_context_dir() { local self="${0#sourcedriver_*}" local ref="${1}" local rootfs="${2}" local out_dir="${3}" local manifest_dir="${4}" if [ -n "${CONTEXT_DIR}" ]; then _debug "$self: writing to $out_dir and $manifest_dir" local tarname="context.tar" _tar -C "${CONTEXT_DIR}" \ --mtime=@0 --owner=0 --group=0 --mode='a+rw' --no-xattrs --no-selinux --no-acls \ -cf "${out_dir}/${tarname}" . local mimetype="$(file --brief --mime-type ${out_dir}/${tarname})" local source_info="${manifest_dir}/${tarname}.json" jq \ -n \ --arg name "${tarname}" \ --arg mimetype "${mimetype}" \ ' { "source.artifact.name": $name, "source.artifact.mimetype": $mimetype } ' \ > "${source_info}" ret=$? if [ $ret -ne 0 ] ; then return 1 fi fi } # # If the caller specified a extra directory # # slightly special driver, as it has a flag/env passed in, that it uses # sourcedriver_extra_src_dir() { local self="${0#sourcedriver_*}" local ref="${1}" local rootfs="${2}" local out_dir="${3}" local manifest_dir="${4}" if [ -n "${EXTRA_SRC_DIR}" ]; then _debug "$self: writing to $out_dir and $manifest_dir" local tarname="extra-src.tar" _tar -C "${EXTRA_SRC_DIR}" \ --mtime=@0 --owner=0 --group=0 --mode='a+rw' --no-xattrs --no-selinux --no-acls \ -cf "${out_dir}/${tarname}" . local mimetype="$(file --brief --mime-type ${out_dir}/${tarname})" local source_info="${manifest_dir}/${tarname}.json" jq \ -n \ --arg name "${tarname}" \ --arg mimetype "${mimetype}" \ ' { "source.artifact.name": $name, "source.artifact.mimetype": $mimetype } ' \ > "${source_info}" ret=$? if [ $ret -ne 0 ] ; then return 1 fi fi } main() { _init ${@} local base_dir="$(pwd)/${ABV_NAME}" # using the bash builtin to parse while getopts ":hplDc:r:e:o:b:d:" opts; do case "${opts}" in b) base_dir="${OPTARG}" ;; c) local context_dir=${OPTARG} ;; e) local extra_src_dir=${OPTARG} ;; r) local rpm_dir=${OPTARG} ;; o) local output_dir=${OPTARG} ;; d) drivers=${OPTARG} ;; l) list_drivers=1 ;; p) push=1 ;; D) export DEBUG=1 ;; h) _usage ;; *) _usage ;; esac done shift $((OPTIND-1)) if [ -n "${list_drivers}" ] ; then set | grep '^sourcedriver_.* () ' | tr -d ' ()' exit 0 fi export CONTEXT_DIR="${CONTEXT_DIR:-$context_dir}" export EXTRA_SRC_DIR="${EXTRA_SRC_DIR:-$extra_src_dir}" export RPM_DIR="${RPM_DIR:-$rpm_dir}" local output_dir="${OUTPUT_DIR:-$output_dir}" local src_dir="${base_dir}/src" local work_dir="${base_dir}/work" export TMPDIR="${work_dir}/tmp" if [ -d "${TMPDIR}" ] ; then _rm_rf "${TMPDIR}" fi mkdir -p "${TMPDIR}" IMAGE_REF="${1}" _debug "IMAGE_REF: ${IMAGE_REF}" IMAGE_REF_BASE="$(parse_img_base ${IMAGE_REF})" _debug "IMAGE_REF_BASE: ${IMAGE_REF_BASE}" IMAGE_TAG="$(parse_img_tag ${IMAGE_REF})" _debug "IMAGE_TAG: ${IMAGE_TAG}" IMAGE_DIGEST="$(parse_img_digest ${IMAGE_REF})" # determine missing digest before fetch, so that we fetch the precise image # including its digest. if [ -z "${IMAGE_DIGEST}" ] ; then IMAGE_DIGEST="$(fetch_img_digest ${IMAGE_REF_BASE}:${IMAGE_TAG})" fi _debug "IMAGE_DIGEST: ${IMAGE_DIGEST}" # if inspect and fetch image, then to an OCI layout dir if [ ! -d "${work_dir}/layouts/${IMAGE_DIGEST/:/\/}" ] ; then # we'll store the image to a path based on its digest, that it can be reused img_layout="$(fetch_img ${IMAGE_REF_BASE}:${IMAGE_TAG}@${IMAGE_DIGEST} ${work_dir}/layouts/${IMAGE_DIGEST/:/\/} )" else img_layout="${work_dir}/layouts/${IMAGE_DIGEST/:/\/}:${IMAGE_TAG}" fi _debug "image layout: ${img_layout}" # setup rootfs, from that OCI layout local unpack_dir="${work_dir}/unpacked/${IMAGE_DIGEST/:/\/}" if [ ! -d "${unpack_dir}" ] ; then unpack_img ${img_layout} ${unpack_dir} fi _debug "unpacked dir: ${unpack_dir}" # clear prior driver's info about source to insert into Source Image _rm_rf "${work_dir}/driver/${IMAGE_DIGEST/:/\/}" if [ -n "${drivers}" ] ; then # clean up the args passed by the caller ... drivers="$(echo ${drivers} | tr ',' ' '| tr '\n' ' ')" else drivers="$(set | grep '^sourcedriver_.* () ' | tr -d ' ()' | tr '\n' ' ')" fi # iterate on the drivers #for driver in sourcedriver_rpm_fetch ; do for driver in ${drivers} ; do _info "calling $driver" mkdir -vp "${src_dir}/${IMAGE_DIGEST/:/\/}/${driver#sourcedriver_*}" mkdir -vp "${work_dir}/driver/${IMAGE_DIGEST/:/\/}/${driver#sourcedriver_*}" $driver \ "${IMAGE_REF_BASE}:${IMAGE_TAG}@${IMAGE_DIGEST}" \ "${unpack_dir}/rootfs" \ "${src_dir}/${IMAGE_DIGEST/:/\/}/${driver#sourcedriver_*}" \ "${work_dir}/driver/${IMAGE_DIGEST/:/\/}/${driver#sourcedriver_*}" if [ $? -ne 0 ] ; then _error "$driver failed" fi # TODO walk the driver output to determine layers to be added # find "${work_dir}/driver/${IMAGE_DIGEST/:/\/}/${driver#sourcedriver_*}" -type f -name '*.json' done # TODO maybe look to a directory like /usr/libexec/BuildSourceImage/drivers/ for drop-ins to run # TODO commit the image # This is going to be a hand craft of composing these layers using just bash and jq echo "bailing here for now" return 0 ## if an output directory is provided then save a copy to it if [ -n "${output_dir}" ] ; then mkdir -p "${output_dir}" # XXX WIP push_img $src_img_dir "oci:$output_dir:$(ref_src_img_tag ${IMAGE_TAG})" fi if [ -n "${push}" ] ; then # XXX WIP push_img $src_dir $IMAGE_REF fi # # For each SRC_RPMS used to build the executable image, download the SRC RPM # and generate a layer in the SRC RPM. # pushd ${SRC_RPM_DIR} > /dev/null export SRC_CTR=$(buildah from scratch) for i in ${SRC_RPMS}; do if [ ! -f $i ]; then RPM=$(echo $i | sed 's/.src.rpm$//g') dnf download --release $RELEASE --source $RPM || continue # TODO: perhaps log failures somewhere fi echo "Adding ${srpm}" touch --date=@`rpm -q --qf '%{buildtime}' ${srpm}` ${srpm} buildah add ${SRC_CTR} ${srpm} /RPMS/ buildah config --created-by "/bin/sh -c #(nop) ADD file:$(sha256sum ${srpm} | cut -f1 -d' ') in /RPMS" ${SRC_CTR} export IMG=$(buildah commit --omit-timestamp --disable-compression --rm ${SRC_CTR}) export SRC_CTR=$(buildah from ${IMG}) done popd > /dev/null } # only exec main if this is being called (this way we can source and test the functions) _is_sourced || main ${@} # vim:set shiftwidth=4 softtabstop=4 expandtab: