Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 79 additions & 11 deletions .ci/pipelines/lib/testing.sh
Original file line number Diff line number Diff line change
Expand Up @@ -181,20 +181,29 @@ testing::is_main_coverage_nightly() {
}

# RHIDP-13242: Prints the main commit the RHDH image in a namespace was built from,
# or nothing. Only the main OCP Helm nightly deploying `next` qualifies; the commit
# or nothing. Only the main OCP Helm nightly deploying `next` qualifies. The commit
# is the image's org.opencontainers.image.revision label, read from the digest the
# pods actually ran, so a `next` rebuilt mid-job does not change it.
# pods actually ran, so a `next` rebuilt mid-job does not change it. Konflux images
# (quay.io/rhdh/rhdh-hub-*) label the GitLab midstream commit instead; for those the
# upstream commit comes from the app's build metadata.
# Args:
# $1 - namespace: where the tested RHDH runs
# Uses globals: JOB_NAME, TAG_NAME, IMAGE_REGISTRY, IMAGE_REPO, DIR
testing::_coverage_main_commit() {
local namespace="${1:-}"
testing::is_main_coverage_nightly && [[ "${TAG_NAME:-}" == "next" && -n "${namespace}" ]] || return 0

local image="${IMAGE_REGISTRY:-quay.io}/${IMAGE_REPO:-rhdh-community/rhdh}" image_ids
image_ids=$(timeout 60 oc get pods -n "${namespace}" \
-o jsonpath='{range .items[*].status.containerStatuses[*]}{.image}{" "}{.imageID}{"\n"}{end}' 2> /dev/null \
| awk -v image="${image}" 'index($1, image ":") == 1 || index($1, image "@") == 1 { print $2 }' | sort -u)
local image="${IMAGE_REGISTRY:-quay.io}/${IMAGE_REPO:-rhdh-community/rhdh}" containers image_ids
# One "<pod>\t<container>\t<imageID>\t<live>" line per container that runs the
# image and has pulled it. live is true for a running container in a pod that is
# not being deleted, so `oc exec` never targets a terminating one.
containers=$(timeout 60 oc get pods -n "${namespace}" -o json 2> /dev/null \
| jq -r --arg image "${image}" '.items[] | .metadata.name as $pod
| (.metadata.deletionTimestamp == null) as $kept | .status.containerStatuses[]?
| select(.imageID != "")
| select((.image | startswith($image + ":")) or (.image | startswith($image + "@")))
| [$pod, .name, .imageID, ($kept and .state.running != null)] | @tsv' 2> /dev/null)
image_ids=$(cut -f3 <<< "${containers}" | sort -u)
if [[ -z "${image_ids}" || "${image_ids}" == *$'\n'* ]]; then
Comment thread
gustavolira marked this conversation as resolved.
log::warn "Expected one ${image} digest in ${namespace}, found: ${image_ids:-none}"
return 0
Expand All @@ -203,15 +212,74 @@ testing::_coverage_main_commit() {
local sha
sha=$(timeout 60 skopeo inspect --override-os linux --override-arch amd64 "docker://${image_ids#docker-pullable://}" 2> /dev/null \
| jq -r '.Labels["org.opencontainers.image.revision"] // empty' 2> /dev/null)
if [[ ! "${sha}" =~ ^[0-9a-f]{40}$ ]]; then
log::warn "Could not read a commit revision label from ${image_ids}"
if [[ "${sha}" =~ ^[0-9a-f]{40}$ ]] && testing::_commit_on_main "${sha}"; then
echo "${sha}"
return 0
fi
if ! testing::_commit_on_main "${sha}"; then
log::warn "Could not confirm that main contains image revision ${sha}"

local pod container upstream
read -r pod container < <(awk -F '\t' '$4 == "true" { print $1, $2; exit }' <<< "${containers}") || true
if [[ -z "${pod}" ]]; then
log::warn "Could not find a main commit for ${image_ids}: revision label ${sha:-none} is not confirmed on main, and no running pod has the build metadata"
return 0
fi
upstream=$(testing::_build_metadata_upstream "${namespace}" "${pod}" "${container}")
if [[ -z "${upstream}" ]]; then
log::warn "Could not find a main commit for ${image_ids}: revision label ${sha:-none} is not confirmed on main, and the build metadata gave no upstream commit"
return 0
fi
if ! testing::_commit_on_main "${upstream}"; then
log::warn "Could not confirm that main contains upstream commit ${upstream} of ${image_ids}"
return 0
fi
log::info "Revision label ${sha:-none} is not confirmed on main; using upstream commit ${upstream} from the build metadata"
echo "${upstream}"
}

# RHIDP-13242: Prints the full upstream main commit named by the app's build metadata
# (".../redhat-developer/rhdh/tree/main @ <short sha>"), which is minified into the
# bundle the pod ships, or nothing. Logs why when it prints nothing.
# Args:
# $1 - namespace
# $2 - pod
# $3 - container: the one that runs the RHDH image
# Uses globals: DIR
testing::_build_metadata_upstream() {
local namespace="$1" pod="$2" container="$3"
local dist="/opt/app-root/src/packages/app/dist" matches err status=0 short full body
err=$(mktemp)
matches=$(timeout 60 oc exec -n "${namespace}" "${pod}" -c "${container}" -- \
grep -rhoE 'redhat-developer/rhdh/tree/main ?@ ?[0-9a-f]{7,40}' "${dist}" 2> "${err}") || status=$?
# grep exits 1, silently, when nothing matches; anything else is a failed read.
if [[ ${status} -ne 0 && (${status} -ne 1 || -s "${err}") ]]; then
log::warn "Could not read the build metadata in ${pod}/${container} (exit ${status})"
log::debug "$(< "${err}")"
rm -f "${err}"
return 0
fi
rm -f "${err}"
short=$(grep -oE '[0-9a-f]{7,40}$' <<< "${matches}" | sort -u | head -1)
if [[ -z "${short}" ]]; then
log::warn "The build metadata in ${pod}/${container} names no redhat-developer/rhdh main commit"
return 0
fi
if full=$(git -C "${DIR}" rev-parse --verify -q "${short}^{commit}" 2> /dev/null); then
echo "${full}"
return 0
fi
if ! body=$(curl -fsS --max-time 30 "https://api.github.com/repos/redhat-developer/rhdh/commits/${short}" 2> /dev/null); then
log::warn "GitHub did not return upstream commit ${short} from the build metadata"
return 0
fi
if ! full=$(jq -r '.sha // empty' <<< "${body}" 2> /dev/null); then
log::warn "GitHub's response for upstream commit ${short} is not JSON"
return 0
fi
if [[ ! "${full}" =~ ^[0-9a-f]{40}$ ]]; then
log::warn "GitHub's response for upstream commit ${short} names no commit sha"
return 0
fi
echo "${sha}"
echo "${full}"
}

# Succeeds when main contains the commit. Uses the checkout (the periodic clones
Expand Down
22 changes: 15 additions & 7 deletions docs/coverage/e2e-rhdh.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,23 @@ the UI have migrated; new specs should import from `@support/coverage/test`.
- `main` gets the flag from the `periodic-ci-redhat-developer-rhdh-main-e2e-ocp-helm-nightly`
job, which `handle_ocp_nightly` (`.ci/pipelines/jobs/ocp-nightly.sh`) switches to
`COLLECT_COVERAGE=true`. PR uploads cannot cover `main`: a squash merge creates a new
commit there. `testing::_coverage_main_commit` takes the image digest the RHDH pods in
the tested namespace ran, reads its `org.opencontainers.image.revision` label with
`skopeo`, checks that `main` contains that commit, and uploads with `--branch main`.
The label, not the checkout, names the commit, because `next` can lag `main`.
commit there. `testing::_coverage_main_commit` takes the digest of the image the job
deploys (`IMAGE_REGISTRY`/`IMAGE_REPO`) from the RHDH pods in the tested namespace,
reads its `org.opencontainers.image.revision` label with `skopeo`, checks that `main`
contains that commit, and uploads with `--branch main`.
A Konflux image such as `quay.io/rhdh/rhdh-hub-rhel10:next` labels the GitLab
midstream commit instead. When the label is not on `main`, the upstream commit comes
from the `.../redhat-developer/rhdh/tree/main @ <sha>` entry of the build metadata
that the downstream build minifies into the bundle the app ships. It is read with
`oc exec` from a running pod and expanded to a full sha through the checkout or GitHub.
The commit always comes from the image, never from the checkout's HEAD, because
`next` can lag `main`.
`next` is also rebuilt on every merge, so the first project's commit is kept for the
job and a project that ran a newer image skips its upload. Other
nightlies, a tag other than `next`, a missing label, or a commit outside `main` skip
the upload. Codecov carries a flag forward from a commit's parent
(`carryforward: true` in `codecov.yml`), so commits processed before the upload do
nightlies, a tag other than `next`, or an image whose label and build metadata both
fail to name a commit on `main` skip the upload. Codecov
carries a flag forward from a commit's parent (`carryforward: true` in
`codecov.yml`), so commits processed before the upload do
not get it: the flag first shows once an upload lands on the newest processed
commit, and later unit-only commits keep it from then on.
- The repository-wide `project`/`patch` statuses and components in `codecov.yml`
Expand Down
Loading
Loading