Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ spec:
value: tasks/managed/populate-release-notes/populate-release-notes.yaml
runAfter:
- verify-conforma
- collect-task-params
- name: check-data-keys
params:
- name: dataPath
Expand Down Expand Up @@ -408,8 +409,8 @@ spec:
- name: quayURL
value: "$(params.quayURL)"
runAfter:
- verify-conforma
- embargo-check
- verify-conforma
- name: create-advisory
Comment thread
qodo-app-for-konflux-ci[bot] marked this conversation as resolved.
params:
- name: releasePlanAdmissionPath
Expand Down
2 changes: 1 addition & 1 deletion tasks/internal/create-advisory-task/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ request.
| advisory_secret_name | The name of the secret that contains the advisory creation metadata | No | - |
| errata_secret_name | The name of the secret that contains the errata service account metadata | No | - |
| internalRequestPipelineRunName | Name of the PipelineRun that called this task | No | - |
| contentType | The contentType of the release artifact. One of [image|binary|generic|rpm] | Yes | image |
| contentType | The contentType of the release artifact. One of [image|binary|generic|rpm|disk-image] | Yes | image |
| caTrustConfigMapName | The name of the ConfigMap to read CA bundle data from | Yes | trusted-ca |
| caTrustConfigMapKey | The name of the key in the ConfigMap that contains the CA bundle data | Yes | ca-bundle.crt |
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ spec:
description: Name of the PipelineRun that called this task
- name: contentType
type: string
description: The contentType of the release artifact. One of [image|binary|generic|rpm]
description: The contentType of the release artifact. One of [image|binary|generic|rpm|disk-image]
default: "image"
- name: caTrustConfigMapName
type: string
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: test-create-advisory-disk-image-content
spec:
description: |
Run the create-advisory task with the contentType param set as disk-image
and check that an advisory url is emitted as a task result
tasks:
- name: setup
taskSpec:
results:
- name: advisory_json
type: string
steps:
- name: generate-advisory-json
image: registry.access.redhat.com/ubi9/ubi-minimal:latest
script: |
#!/usr/bin/env bash
set -euo pipefail
# advisory_json is the gzip+base64 encoding of:
# {"product_id":123,"product_name":"Red Hat Product","product_version":"1.2.3",
# "product_stream":"tp1","cpe":"cpe:/a:example:product:el8","type":"RHSA",
# "synopsis":"test synopsis","topic":"test topic","description":"test description",
# "solution":"test solution","references":["https://docs.example.com/notes"],
# "content":{"artifacts":[{"architecture":"x86_64","os":"linux","component":"test-disk-image",
# "purl":"pkg:generic/test-disk-image@1.0.0?filename=test-image.iso",
# "signingKey":"redhatrelease2"}]}}
advisory_b64="H4sIAAAAAAAAA12RT2sCMRDF736KJeeadbVIWShtb0IvxR6LSEjGdXA3CclsUcTv"
advisory_b64+="3vxD1p6SvN+bNzPkOqsqZp1Ro6Q9KtZWzXL1NBW1GCDIbAuq2giqvrLOHky/4Dwa"
advisory_b64+="HX0NX/LVI/XkQAwRkm0ykjaFhqOtRQtnMdge2lLQQv+SbXTJvu3m+yMr/qKN9ehTG"
advisory_b64+="niq7kIuMBblneVXAgq8dGipTJnwVMvhph8fHHchYQcHcKAlxO4/7EhkfVvXykjPywp"
advisory_b64+="cmqHWJhSzXV7UaAJNoeAankEQjvAgJKWMJFUFFSyPSCBpdGnz88t6v35O/YvDpN171"
advisory_b64+="ON5KofG1ujcKg0/V+hPcxxEB1OfHV0fLfbUtR1ocCjrf/b3hi/44u2APcTff004EY7e"
advisory_b64+="TMM8dhp19wmXGOlAHUX46x6EhyUrtls6d7N4u/0BSEfEJnACAAA="
echo -n "${advisory_b64}" > "$(results.advisory_json.path)"
- name: run-task
runAfter:
- setup
taskRef:
name: create-advisory-task
params:
- name: advisory_json
value: $(tasks.setup.results.advisory_json)
- name: componentGroup
value: "test-app"
- name: origin
value: "not-existing-origin"
- name: config_map_name
value: "create-advisory-test-cm"
- name: advisory_secret_name
value: "create-advisory-secret"
- name: errata_secret_name
value: "create-advisory-errata-secret"
- name: internalRequestPipelineRunName
value: $(context.pipelineRun.name)
- name: contentType
value: "disk-image"
Comment thread
swickersh marked this conversation as resolved.
- name: check-result
runAfter:
- run-task
params:
- name: result
value: $(tasks.run-task.results.result)
- name: advisory_url
value: $(tasks.run-task.results.advisory_url)
taskSpec:
params:
- name: result
type: string
- name: advisory_url
type: string
steps:
- name: check-result
image: registry.access.redhat.com/ubi9/ubi-minimal:latest
script: |
#!/usr/bin/env bash
set -euxo pipefail

echo Test that result is Success
test "$(params.result)" == Success

echo Test that advisory_url was properly set
url="$(params.advisory_url)"
if [[ "${url}" != https://access.redhat.com/errata/RHSA-*:1234 ]]; then
echo "Unexpected advisory URL: ${url}"
exit 1
fi
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ spec:

steps:
- name: push-artifacts-to-cdn
image: quay.io/konflux-ci/release-service-utils@sha256:67bb57a671d7e1b8d51ee07cefcca4ac46db02b8c3a9ee8fad60fb9365aab324
# TODO(RELEASE-2460): temporary image from PR #833 branch; replace with the released
# quay.io/konflux-ci/release-service-utils digest once PR #833 is merged and published.
Comment thread
FilipNikolovski marked this conversation as resolved.
image: quay.io/redhat-user-workloads/rhtap-release-2-tenant/release-service-utils-standalone@sha256:2958c29f7ebd57fe7ca54724bbc5f883946c7b4f92ed1fca810d57072fde87f0
Comment thread
swickersh marked this conversation as resolved.
securityContext:
runAsUser: 1001
computeResources:
Expand Down
153 changes: 112 additions & 41 deletions tasks/managed/create-advisory/create-advisory.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,16 @@ spec:

COMPONENTS=$(jq -c '.mapping.components[]' "$DATA_FILE")

# Build a name → staged.files lookup from the snapshot. apply-mapping substitutes
# {{ release_timestamp }} (and other template vars) in the snapshot's components but
# does NOT write those substitutions back to the data file. Reading staged.files from
# DATA_FILE would produce filenames like "foo-{{ release_timestamp }}-x86_64.iso" which
# Jinja2 would then render as empty, giving "foo--x86_64.iso" in the advisory.
SNAPSHOT_FILE="$(params.dataDir)/$(params.snapshotPath)"
SNAPSHOT_STAGED=$(jq -c \
'reduce .components[] as $c ({}; .[$c.name] = ($c.staged.files // []))' \
"$SNAPSHOT_FILE")

Comment thread
swickersh marked this conversation as resolved.
UPDATED_ENTRIES=()

while IFS= read -r component; do
Expand All @@ -306,47 +316,96 @@ spec:
continue
fi

while IFS= read -r entry; do
ARCH=$(jq -r '.architecture' <<< "$entry")
OS=$(jq -r '.os' <<< "$entry")
if [ "$content_type" = "disk-image" ]; then
# For disk-images, generate one advisory entry per file in staged.files[].
# Multiple files can share the same os+arch (e.g. ISO + QCOW2 both linux/amd64),
# so iterating over advisory entries (one per os+arch) would produce malformed PURLs.
# Instead, use the first advisory entry as a metadata template and expand it per file.
TEMPLATE_ENTRY=$(echo "$MATCHING_ENTRIES" | head -1)
# When a component has both contentGateway (Developer Portal) and staged (Customer
# Portal/CDN), CGW is used as the canonical download_url in the PURL. If only
# staged is present, the CDN URL is used instead.
HAS_CGW=$(jq -r 'if ((.contentGateway // {}) | length) > 0 then "true" else "false" end' \
<<< "$component")
if [ "$HAS_CGW" = "true" ]; then
DOWNLOAD_URL="$CGW_BASE_URL"
else
DOWNLOAD_URL="$CDN_BASE_URL"
fi

while IFS= read -r staged_file; do
FILENAME=$(jq -r '.filename' <<< "$staged_file")
FILENAME_BASENAME=$(basename "$FILENAME")

if [ "$content_type" = "disk-image" ]; then
# For disk-images, we need to match the filename from the populate-release-notes step
# with the staged.files[].filename that has the same architecture
FILENAME=$(jq -r --arg arch "$ARCH" \
'.staged.files[] | select(.filename | contains($arch)) | .filename' <<< "$component")
# Derive arch from filename (same logic as populate-release-notes)
FILE_ARCH="unknown"
if [[ "$FILENAME_BASENAME" == *"aarch64"* ]]; then
FILE_ARCH="aarch64"
elif [[ "$FILENAME_BASENAME" == *"x86_64"* ]]; then
FILE_ARCH="x86_64"
fi
FILE_OS="linux" # Default for disk images

if [ -z "$FILENAME" ] || [ "$FILENAME" = "null" ]; then
echo "Warning: No filename found for arch $ARCH in component $COMPONENT_NAME" >&2
continue
fi
else
# Binary/generic files have arch/os matching, falling back to staged.files
# for teams that use the CDN staged structure instead of a top-level files array
FILENAME=$(jq -r --arg arch "$ARCH" --arg os "$OS" \
'((.files // []) as $f
| if ($f|length) > 0 then $f else (.staged.files // []) end)[]
| select(.arch == $arch and .os == $os) | .source' \
<<< "$component")
CHECKSUM=$(jq -r --arg component "$COMPONENT_NAME" --arg filename "$FILENAME_BASENAME" \
'.[] | select(.component == $component) | .files[$filename] // empty' \
<<< "$CHECKSUM_MAP_JSON")
if [ -z "$CHECKSUM" ]; then
echo "Warning: No checksum found for $COMPONENT_NAME/$FILENAME_BASENAME in manifest" >&2
fi

PURL="pkg:generic/$COMPONENT_NAME@$VERSION_NAME"
QUERY_PARAMS=()
# Add filename first - this uniquely identifies the file
if [ -n "$FILENAME_BASENAME" ]; then
QUERY_PARAMS+=("filename=$FILENAME_BASENAME")
fi
if [ -n "$CHECKSUM" ]; then
QUERY_PARAMS+=("checksum=$CHECKSUM")
fi
if [ -n "$DOWNLOAD_URL" ]; then
QUERY_PARAMS+=("download_url=$DOWNLOAD_URL")
fi
if [ "${#QUERY_PARAMS[@]}" -gt 0 ]; then
PURL="${PURL}?$(IFS=\&; echo "${QUERY_PARAMS[*]}")"
fi

UPDATED_ENTRY=$(jq -c \
--arg purl "$PURL" --arg arch "$FILE_ARCH" --arg os "$FILE_OS" \
'.purl = $purl | .architecture = $arch | .os = $os' <<< "$TEMPLATE_ENTRY")
UPDATED_ENTRIES+=("$UPDATED_ENTRY")
done <<< "$(jq -c --arg name "$COMPONENT_NAME" '.[$name][]?' <<< "$SNAPSHOT_STAGED")"
Comment thread
qodo-app-for-konflux-ci[bot] marked this conversation as resolved.
else
while IFS= read -r entry; do
ARCH=$(jq -r '.architecture' <<< "$entry")
OS=$(jq -r '.os' <<< "$entry")

# Binary/generic files have arch/os matching, falling back to staged.files
# for teams that use the CDN staged structure instead of a top-level files array
FILENAME=$(jq -r --arg arch "$ARCH" --arg os "$OS" \
'((.files // []) as $f
| if ($f|length) > 0 then $f else (.staged.files // []) end)[]
| select(.arch == $arch and .os == $os) | .source' \
<<< "$component")

FILENAME_BASENAME=$(basename "$FILENAME")
# Apply Windows archive conversion (.tar.gz -> .zip) when looking up checksum
# This matches the conversion done in compress-artifacts step of push-artifacts-to-cdn-task
if [ "$OS" = "windows" ] && [[ "$FILENAME_BASENAME" == *.tar.gz ]]; then
FILENAME_BASENAME="${FILENAME_BASENAME%.tar.gz}.zip"
fi

CHECKSUM=$(jq -r --arg component "$COMPONENT_NAME" --arg filename "$FILENAME_BASENAME" \
'.[] | select(.component == $component) | .files[$filename] // empty' <<< "$CHECKSUM_MAP_JSON")
'.[] | select(.component == $component) | .files[$filename] // empty' \
<<< "$CHECKSUM_MAP_JSON")
if [ -z "$CHECKSUM" ]; then
echo "Warning: No checksum found for $COMPONENT_NAME/$FILENAME_BASENAME in manifest" >&2
fi

# Determine download URL based on portal (using environment-aware base URLs)
# - If .contentGateway: Developer Portal (CGW)
# - If only .staged (no .contentGateway): Customer Portal (CDN)
HAS_CGW=$(jq -r '.contentGateway | length > 0' <<< "$component")
# When a component has both contentGateway (Developer Portal) and staged (Customer
# Portal/CDN), CGW is used as the canonical download_url in the PURL. If only
# staged is present, the CDN URL is used instead.
HAS_CGW=$(jq -r 'if ((.contentGateway // {}) | length) > 0 then "true" else "false" end' \
<<< "$component")
if [ "$HAS_CGW" = "true" ]; then
DOWNLOAD_URL="$CGW_BASE_URL"
else
Expand All @@ -371,30 +430,42 @@ spec:

UPDATED_ENTRY=$(jq -c --arg purl "$PURL" '.purl = $purl' <<< "$entry")
UPDATED_ENTRIES+=("$UPDATED_ENTRY")
done <<< "$MATCHING_ENTRIES"
done <<< "$MATCHING_ENTRIES"
fi

done <<< "$COMPONENTS"

# Merge updated advisory entries back into releaseNotes.content.artifacts in the DATA_FILE
# Also deduplicate entries by component|architecture|os to handle cases where
# multiple files have the same arch/os (e.g., binary + ISO for same arch)
if [ "${#UPDATED_ENTRIES[@]}" -eq 0 ]; then
echo "No advisory entries were updated."
exit 0
fi

ENTRIES_JOINED=$(IFS=,; echo "${UPDATED_ENTRIES[*]}")
UPDATED_MAP=$(jq -nc --argjson updated "[$ENTRIES_JOINED]" '
reduce $updated[] as $item ({}; . + {($item.component + "|" + $item.architecture + "|" + $item.os): $item})
')
# Update entries AND deduplicate: convert to map (dedup by key), then back to array
jq --argjson updated "$UPDATED_MAP" '
.releaseNotes.content.artifacts |= (
map(($updated[(.component + "|" + .architecture + "|" + .os)] // .))
| reduce .[] as $item ({}; . + {($item.component + "|" + $item.architecture + "|" + $item.os): $item})
| [.[]]
)
' "$DATA_FILE" > /tmp/data.tmp && mv /tmp/data.tmp "$DATA_FILE"
if [ "$content_type" = "disk-image" ]; then
# For disk-images, drop all existing advisory entries for the updated components and
# replace them with the new per-file entries. We cannot deduplicate by component|arch|os
# because multiple files (e.g. ISO + QCOW2) legitimately share the same arch/os.
jq --argjson updated "[$ENTRIES_JOINED]" '
($updated | map(.component) | unique) as $updated_components |
.releaseNotes.content.artifacts |= (
map(select(.component | IN($updated_components[]) | not)) + $updated
)
' "$DATA_FILE" > /tmp/data.tmp && mv /tmp/data.tmp "$DATA_FILE"
else
# Merge updated advisory entries back into releaseNotes.content.artifacts in the DATA_FILE.
# Deduplicate by component|architecture|os to handle cases where multiple files share the
# same arch/os (e.g. binary + ISO for same arch).
UPDATED_MAP=$(jq -nc --argjson updated "[$ENTRIES_JOINED]" '
reduce $updated[] as $item ({}; . + {($item.component + "|" + $item.architecture + "|" + $item.os): $item})
')
jq --argjson updated "$UPDATED_MAP" '
.releaseNotes.content.artifacts |= (
map(($updated[(.component + "|" + .architecture + "|" + .os)] // .))
| reduce .[] as $item ({}; . + {($item.component + "|" + $item.architecture + "|" + $item.os): $item})
| [.[]]
)
' "$DATA_FILE" > /tmp/data.tmp && mv /tmp/data.tmp "$DATA_FILE"
fi
- name: run-script
image: quay.io/konflux-ci/release-service-utils@sha256:5546fa78d3c88d7b6a2e8cff8902f7757f00541d0bbaf113b9f293133894afa3
computeResources:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,15 @@ spec:
"components": [
{
"name": "intel-bootc-iso-disk-image-1-5",
"repository": "quay.io/redhat-prod/rhel-ai----disk-image"
"repository": "quay.io/redhat-prod/rhel-ai----disk-image",
"staged": {
"files": [
{
"filename": "rhel-ai-intel-1.5.1-1749643937-x86_64.iso.gz",
"source": "install.iso.gz"
}
]
}
}
]
}
Expand Down
Loading
Loading