From dd2b0e2ca7c901fc5be6c41b085776956cbc7acc Mon Sep 17 00:00:00 2001 From: ryosuke-nakayama Date: Fri, 3 Oct 2025 11:02:38 +0900 Subject: [PATCH 01/14] Add a changelog feature that shows the RPM diff compared to the previous release in the container build workflow --- .github/workflows/build-test-push.yml | 289 +++++++++++++++++++++++++- 1 file changed, 288 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index 31bb7f5..e2403b4 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -87,7 +87,6 @@ env: registries_production: 'docker.io/almalinux, quay.io/almalinuxorg, ghcr.io/almalinux' registries_testing: 'quay.io/almalinuxautobot' - jobs: init-data: name: Set matrix, prepare variables @@ -348,6 +347,181 @@ jobs: && ( test "${{ matrix.image_types }}" != "micro" && rpm -q gpg-pubkey) || true " done + - + name: Detect previous released tag for this image type + if: env.check_update != 0 + id: detect-prev + env: + VMJ: ${{ matrix.version_major }} + IMT: ${{ matrix.image_types }} + VMIN: ${{ env.version_minor }} + DATE: ${{ env.date_stamp }} + run: | + IMAGE_REPO="${IMAGE_NAMES%%,*}" + + # Fetch tag list and pick latest dated tag (-YYYYMMDD + TAG_JSON=$(skopeo list-tags docker://"${IMAGE_REPO}" || echo '{}') + CAND=$(echo "${TAG_JSON}" | jq -r '.Tags // [] | .[]' | grep -E "^${VMJ}${VMIN}-[0-9]{8}$" | sort -r | while read -r T; do + D="${T##*-}" + if [ "$D" -lt "${DATE}" ]; then + echo "$T" + break + fi + done) + + # If none found, treat as first release (no comparison target) + if [ -z "${CAND}" ]; then + echo "prev_image=" >> $GITHUB_ENV + echo "prev_tag=" >> $GITHUB_ENV + echo "prev_found=false" >> $GITHUB_OUTPUT + echo "No previous dated tag found for ${IMAGE_REPO}" >&2 + exit 0 + fi + + echo "prev_image=${IMAGE_REPO}" >> $GITHUB_ENV + echo "prev_tag=${CAND}" >> $GITHUB_ENV + echo "prev_found=true" >> $GITHUB_OUTPUT + echo "Previous: ${IMAGE_REPO}:${CAND}" + + - + name: Generate RPM lists (new vs previous) per platform + if: env.check_update != 0 + id: rpm-lists + env: + IMG_NEW_DIGEST: ${{ steps.build-images.outputs.digest }} + IMG_PREV: ${{ env.prev_image }} + TAG_PREV: ${{ env.prev_tag }} + PLATFORMS: ${{ env.platforms }} + VMJ: ${{ matrix.version_major }} + IMT: ${{ matrix.image_types }} + run: | + set -euo pipefail + mkdir -p rpm_lists/${VMJ}/${IMT} + + list_rpms() { + local ref="$1" plat="$2" outfile="$3" + # Save both package name and full version + docker run --platform="${plat}" --rm "${ref}" /bin/bash -c \ + "rpm -qa --queryformat '%{NAME}\t%{VERSION}-%{RELEASE}\n' | sort" \ + > "${outfile}" + # Also create a names-only list + cut -f1 "${outfile}" | sort -u > "${outfile%.txt}.names" + } + + for p in ${PLATFORMS//,/ }; do + plat=$(echo "$p" | xargs) + + # New image: refer by digest + list_rpms "${IMG_NEW_DIGEST}" "${plat}" "rpm_lists/${VMJ}/${IMT}/new_${plat//\//_}.txt" + + # Old image (if present): : + if [ -n "${IMG_PREV:-}" ] && [ -n "${TAG_PREV:-}" ]; then + docker pull --platform "${plat}" "${IMG_PREV}:${TAG_PREV}" >/dev/null 2>&1 || true + list_rpms "${IMG_PREV}:${TAG_PREV}" "${plat}" "rpm_lists/${VMJ}/${IMT}/old_${plat//\//_}.txt" || \ + : # Continue even if platform not provided previously + fi + done + + - + name: Diff RPM sets and build Markdown changelog + if: env.check_update != 0 + id: rpm-diff + env: + PLATFORMS: ${{ env.platforms }} + VMJ: ${{ matrix.version_major }} + IMT: ${{ matrix.image_types }} + PREV_TAG: ${{ env.prev_tag }} + DTS: ${{ needs.init-data.outputs.date_time_stamp }} + run: | + set -euo pipefail + out="rpm_diff_${VMJ}_${IMT}.md" + echo "**AlmaLinux ${VMJ} ${IMT}** RPM changelog (build \`${DTS}\`)" > "${out}" + if [ -n "${PREV_TAG}" ]; then + echo "" >> "${out}" + echo "- Compared to previous: \`${PREV_TAG}\`" >> "${out}" + else + echo "" >> "${out}" + echo "- No previous dated tag found. Treating all as **Added**." >> "${out}" + fi + echo "" >> "${out}" + + any_section=false + for p in ${PLATFORMS//,/ }; do + plat=$(echo "$p" | xargs) + key="${plat//\//_}" + new="rpm_lists/${VMJ}/${IMT}/new_${key}.txt" + old="rpm_lists/${VMJ}/${IMT}/old_${key}.txt" + + [ ! -f "${new}" ] && continue + + echo "### Platform: \`${plat}\`" >> "${out}" + + if [ -f "${old}" ]; then + # Added / Removed sets based on names + comm -13 "${old%.txt}.names" "${new%.txt}.names" > added.tmp || true + comm -23 "${old%.txt}.names" "${new%.txt}.names" > removed.tmp || true + + # Updated: same name but different versions + join -j1 -t $'\t' -o 0,1.2,2.2 \ + <(join -t $'\t' -1 1 -2 1 <(cut -f1,2 "${old}" | sort) <(cut -f1,2 "${new}" | sort -k1,1) | sort -k1,1) \ + <(cut -f1,2 "${new}" | sort -k1,1) >/dev/null 2>&1 || true + + # Recompute diffs and extract version changes + updated_tmp=$(mktemp) + awk -F'\t' 'NR==FNR{old[$1]=$2;next}{ if($1 in old && old[$1]!=$2){ print $1"\t"old[$1]" -> "$2 } }' \ + "${old}" "${new}" > "${updated_tmp}" + + addc=$(wc -l < added.tmp | tr -d ' ') + updc=$(wc -l < "${updated_tmp}" | tr -d ' ') + delc=$(wc -l < removed.tmp | tr -d ' ') + + echo "" >> "${out}" + echo "**Added (${addc})**" >> "${out}" + echo "" >> "${out}" + [ "$addc" -gt 0 ] && awk '{print "- " $0}' added.tmp >> "${out}" || echo "- (none)" >> "${out}" + + echo "" >> "${out}" + echo "**Updated (${updc})**" >> "${out}" + echo "" >> "${out}" + if [ "$updc" -gt 0 ]; then + awk -F'\t' '{printf "- %s: %s\n",$1,$2}' "${updated_tmp}" >> "${out}" + else + echo "- (none)" >> "${out}" + fi + + echo "" >> "${out}" + echo "**Removed (${delc})**" >> "${out}" + echo "" >> "${out}" + [ "$delc" -gt 0 ] && awk '{print "- " $0}' removed.tmp >> "${out}" || echo "- (none)" >> "${out}" + echo "" >> "${out}" + + any_section=true + rm -f added.tmp removed.tmp "${updated_tmp}" + else + # No previous list: treat all as Added + echo "" >> "${out}" + echo "**Added**" >> "${out}" + echo "" >> "${out}" + cut -f1 "${new}" | awk '{print "- " $0}' >> "${out}" + echo "" >> "${out}" + any_section=true + fi + done + + $any_section || echo "_No RPM changes detected (no comparable platforms found)._" >> "${out}" + + # Move artifacts into a readable path + mkdir -p rpm_diff_artifacts/${VMJ} + mv "${out}" "rpm_diff_artifacts/${VMJ}/rpm_diff_${VMJ}_${IMT}.md" + + - + name: Upload RPM diff artifacts + if: env.check_update != 0 + uses: actions/upload-artifact@v4 + with: + name: rpm-diff-${{ matrix.version_major }}-${{ matrix.image_types }} + path: rpm_diff_artifacts/${{ matrix.version_major }}/ + - name: Push images to Client Library if: env.check_update != 0 @@ -524,6 +698,119 @@ jobs: message: "AlmaLinux ${{ matrix.version_major }} ${{ matrix.image_types }} - ${{ env.date_stamp }} ${{ env.time_stamp }} (generated on ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})." push: true + publish-rpm-diff: + name: Publish RPM diff to GitHub Release + runs-on: ubuntu-24.04 + needs: [init-data, build-test-push] + steps: + - + name: Download all RPM diff artifacts (if any) + uses: actions/download-artifact@v4 + with: + pattern: rpm-diff-* + merge-multiple: true + path: _rpm_diff_all + continue-on-error: true + + - + name: Check artifacts existence + id: chk + run: | + set -euo pipefail + # Recursively search to find any rpm_diff_*.md anywhere + if ! find _rpm_diff_all -type f -name 'rpm_diff_*.md' | grep -q .; then + echo "found=false" >> "$GITHUB_OUTPUT" + echo "No RPM diff artifacts found. Skipping release." + exit 0 + fi + echo "found=true" >> "$GITHUB_OUTPUT" + + - + name: Compose release notes (group by version & type) + if: steps.chk.outputs.found == 'true' + id: notes + run: | + set -euo pipefail + + DATE_STAMP='${{ needs.init-data.outputs.date_stamp }}' + TIME_STAMP='${{ needs.init-data.outputs.time_stamp }}' + PROD='${{ needs.init-data.outputs.production }}' + + header="# AlmaLinux Container Images – RPM diffs (${DATE_STAMP} ${TIME_STAMP} UTC)\n" + [ "${PROD}" = "true" ] && header="${header}\n_Release type: **Production**_\n" || header="${header}\n_Release type: **Testing / Pre-release**_\n" + printf "%b\n" "${header}" > RELEASE_NOTES.md + + # Collect all md files regardless of where they were unpacked + mapfile -t FILES < <(find _rpm_diff_all -type f -name 'rpm_diff_*.md' | sort) + + # Build a map: vmj::type -> file (robustly extracted from file name) + declare -A MAP + declare -A TYPES_OF + for f in "${FILES[@]}"; do + base=$(basename "$f") + if [[ "$base" =~ ^rpm_diff_([0-9]+|10-kitten)_([a-z0-9_-]+)\.md$ ]]; then + vmj="${BASH_REMATCH[1]}" + itype="${BASH_REMATCH[2]}" + key="${vmj}::${itype}" + MAP["$key"]="$f" + TYPES_OF["$vmj"]+="$itype " + fi + done + + # Sort versions ascending; output types in a fixed order for readability + mapfile -t VMJS < <(printf "%s\n" "${!TYPES_OF[@]}" | sort -V) + ORDER=(default minimal micro base init toolbox) + + for vmj in "${VMJS[@]}"; do + echo -e "\n## AlmaLinux ${vmj}\n" >> RELEASE_NOTES.md + + read -r -a tlist <<< "${TYPES_OF[$vmj]}" + + # Output known types in preferred order + for want in "${ORDER[@]}"; do + for itype in "${tlist[@]}"; do + [ "$itype" != "$want" ] && continue + key="${vmj}::${itype}" + f="${MAP[$key]}" + [ -z "$f" ] && continue + echo -e "\n### Image type: \`${itype}\`\n" >> RELEASE_NOTES.md + # Inline the full changelog (Added/Updated/Removed) for each flavor + cat "$f" >> RELEASE_NOTES.md + done + done + # Any unknown types go last + for itype in "${tlist[@]}"; do + if [[ ! " ${ORDER[*]} " =~ " ${itype} " ]]; then + key="${vmj}::${itype}" + f="${MAP[$key]}" + [ -z "$f" ] && continue + echo -e "\n### Image type: \`${itype}\`\n" >> RELEASE_NOTES.md + cat "$f" >> RELEASE_NOTES.md + fi + done + done + + # Also archive the original markdown files for attachment + tar -czf rpm-diff-artifacts-${DATE_STAMP}.tar.gz -C _rpm_diff_all . + + echo "tag=v${DATE_STAMP}" >> "$GITHUB_OUTPUT" + echo "name=AlmaLinux Container RPM diffs (${DATE_STAMP})" >> "$GITHUB_OUTPUT" + + - + name: Create/Update GitHub Release + if: steps.chk.outputs.found == 'true' + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.notes.outputs.tag }} + name: ${{ steps.notes.outputs.name }} + body_path: RELEASE_NOTES.md + draft: false + prerelease: ${{ needs.init-data.outputs.production != 'true' }} + files: | + rpm-diff-artifacts-${{ needs.init-data.outputs.date_stamp }}.tar.gz + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + notify-mattermost: name: Mattermost notification runs-on: ubuntu-24.04 From b63a7742d0070cc5d40f208ab281f0b7dd49f8f4 Mon Sep 17 00:00:00 2001 From: Ryosuke Nakayama <78244973+unixtech-06@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:17:14 +0900 Subject: [PATCH 02/14] =?UTF-8?q?fix(diff):=20change=20arrow=20in=20awk=20?= =?UTF-8?q?output=20from=20'->'=20to=20'=E2=86=92'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alex Iribarren --- .github/workflows/build-test-push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index e2403b4..d523997 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -468,7 +468,7 @@ jobs: # Recompute diffs and extract version changes updated_tmp=$(mktemp) - awk -F'\t' 'NR==FNR{old[$1]=$2;next}{ if($1 in old && old[$1]!=$2){ print $1"\t"old[$1]" -> "$2 } }' \ + awk -F'\t' 'NR==FNR{old[$1]=$2;next}{ if($1 in old && old[$1]!=$2){ print $1"\t"old[$1]" → "$2 } }' \ "${old}" "${new}" > "${updated_tmp}" addc=$(wc -l < added.tmp | tr -d ' ') From b9bca8d40a9c0978b471a105b804d7f330d31a93 Mon Sep 17 00:00:00 2001 From: ryosuke-nakayama Date: Tue, 7 Oct 2025 09:45:06 +0900 Subject: [PATCH 03/14] =?UTF-8?q?ci:=20simplify=20previous=20image=20detec?= =?UTF-8?q?tion=20=E2=80=94=20use=20=20tag=20directly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-test-push.yml | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index d523997..6386adc 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -357,25 +357,13 @@ jobs: VMIN: ${{ env.version_minor }} DATE: ${{ env.date_stamp }} run: | + set -euo pipefail IMAGE_REPO="${IMAGE_NAMES%%,*}" - - # Fetch tag list and pick latest dated tag (-YYYYMMDD - TAG_JSON=$(skopeo list-tags docker://"${IMAGE_REPO}" || echo '{}') - CAND=$(echo "${TAG_JSON}" | jq -r '.Tags // [] | .[]' | grep -E "^${VMJ}${VMIN}-[0-9]{8}$" | sort -r | while read -r T; do - D="${T##*-}" - if [ "$D" -lt "${DATE}" ]; then - echo "$T" - break - fi - done) - - # If none found, treat as first release (no comparison target) - if [ -z "${CAND}" ]; then - echo "prev_image=" >> $GITHUB_ENV - echo "prev_tag=" >> $GITHUB_ENV - echo "prev_found=false" >> $GITHUB_OUTPUT - echo "No previous dated tag found for ${IMAGE_REPO}" >&2 - exit 0 + PREV_TAG="${VMJ}" + echo "Checking ${IMAGE_REPO}:${PREV_TAG}" + if ! skopeo inspect docker://"${IMAGE_REPO}:${PREV_TAG}" >/dev/null 2>&1; then + echo "::error ::Tag not found: ${IMAGE_REPO}:${PREV_TAG}" + exit 1 fi echo "prev_image=${IMAGE_REPO}" >> $GITHUB_ENV From c59a1bc7792d6486f21ec471145dc67a3c79085a Mon Sep 17 00:00:00 2001 From: ryosuke-nakayama Date: Tue, 7 Oct 2025 11:46:58 +0900 Subject: [PATCH 04/14] ci: skip empty RPM diff sections in changelog --- .github/workflows/build-test-push.yml | 51 ++++++++++++++++++--------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index 6386adc..8e187b4 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -463,27 +463,46 @@ jobs: updc=$(wc -l < "${updated_tmp}" | tr -d ' ') delc=$(wc -l < removed.tmp | tr -d ' ') - echo "" >> "${out}" - echo "**Added (${addc})**" >> "${out}" - echo "" >> "${out}" - [ "$addc" -gt 0 ] && awk '{print "- " $0}' added.tmp >> "${out}" || echo "- (none)" >> "${out}" + printed=false + + # Added + if [ "$addc" -gt 0 ]; then + { + echo "" + echo "**Added (${addc})**" + echo "" + awk '{print "- " $0}' added.tmp + echo "" + } >> "${out}" + printed=true + fi - echo "" >> "${out}" - echo "**Updated (${updc})**" >> "${out}" - echo "" >> "${out}" + # Updated if [ "$updc" -gt 0 ]; then - awk -F'\t' '{printf "- %s: %s\n",$1,$2}' "${updated_tmp}" >> "${out}" - else - echo "- (none)" >> "${out}" + { + echo "" + echo "**Updated (${updc})**" + echo "" + awk -F'\t' '{printf "- %s: %s\n",$1,$2}' "${updated_tmp}" + echo "" + } >> "${out}" + printed=true fi - echo "" >> "${out}" - echo "**Removed (${delc})**" >> "${out}" - echo "" >> "${out}" - [ "$delc" -gt 0 ] && awk '{print "- " $0}' removed.tmp >> "${out}" || echo "- (none)" >> "${out}" - echo "" >> "${out}" + # Removed + if [ "$delc" -gt 0 ]; then + { + echo "" + echo "**Removed (${delc})**" + echo "" + awk '{print "- " $0}' removed.tmp + echo "" + } >> "${out}" + printed=true + fi + + [ "$printed" = true ] && any_section=true - any_section=true rm -f added.tmp removed.tmp "${updated_tmp}" else # No previous list: treat all as Added From 5ebb806c16d0c6337ae894c3089775f8233c53bd Mon Sep 17 00:00:00 2001 From: ryosuke-nakayama Date: Tue, 7 Oct 2025 11:51:44 +0900 Subject: [PATCH 05/14] =?UTF-8?q?ci:=20simplify=20previous=20image=20detec?= =?UTF-8?q?tion=20=E2=80=94=20use=20tag=20directly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-test-push.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index 8e187b4..b98d3db 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -366,10 +366,10 @@ jobs: exit 1 fi - echo "prev_image=${IMAGE_REPO}" >> $GITHUB_ENV - echo "prev_tag=${CAND}" >> $GITHUB_ENV - echo "prev_found=true" >> $GITHUB_OUTPUT - echo "Previous: ${IMAGE_REPO}:${CAND}" + echo "prev_image=${IMAGE_REPO}" >> "$GITHUB_ENV" + echo "prev_tag=${PREV_TAG}" >> "$GITHUB_ENV" + echo "prev_found=true" >> "$GITHUB_OUTPUT" + echo "Previous: ${IMAGE_REPO}:${PREV_TAG}" - name: Generate RPM lists (new vs previous) per platform From 8c0ac51ac5f24443a5f6f2985951ca370190a0c7 Mon Sep 17 00:00:00 2001 From: ryosuke-nakayama Date: Tue, 7 Oct 2025 15:39:48 +0900 Subject: [PATCH 06/14] ci: wrap per-platform RPM diff in collapsible
sections --- .github/workflows/build-test-push.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index b98d3db..183d41e 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -442,7 +442,14 @@ jobs: [ ! -f "${new}" ] && continue - echo "### Platform: \`${plat}\`" >> "${out}" + + # Collapse each platform's RPM diff into a
section + { + echo "" + echo "
" + echo "Platform: \`${plat}\`" + echo "" + } >> "${out}" if [ -f "${old}" ]; then # Added / Removed sets based on names @@ -512,6 +519,12 @@ jobs: cut -f1 "${new}" | awk '{print "- " $0}' >> "${out}" echo "" >> "${out}" any_section=true + + { + echo "" + echo "
" + echo "" + } >> "${out}" fi done From d299edb244653119dbc361a6492327e27e182a38 Mon Sep 17 00:00:00 2001 From: ryosuke-nakayama Date: Wed, 8 Oct 2025 09:44:42 +0900 Subject: [PATCH 07/14] ci: allow initial run when no previous tag exists --- .github/workflows/build-test-push.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index 183d41e..bf1aa36 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -353,24 +353,24 @@ jobs: id: detect-prev env: VMJ: ${{ matrix.version_major }} - IMT: ${{ matrix.image_types }} - VMIN: ${{ env.version_minor }} - DATE: ${{ env.date_stamp }} run: | set -euo pipefail IMAGE_REPO="${IMAGE_NAMES%%,*}" PREV_TAG="${VMJ}" + echo "Checking ${IMAGE_REPO}:${PREV_TAG}" - if ! skopeo inspect docker://"${IMAGE_REPO}:${PREV_TAG}" >/dev/null 2>&1; then - echo "::error ::Tag not found: ${IMAGE_REPO}:${PREV_TAG}" - exit 1 + if skopeo inspect docker://"${IMAGE_REPO}:${PREV_TAG}" >/dev/null 2>&1; then + echo "prev_image=${IMAGE_REPO}" >> "$GITHUB_ENV" + echo "prev_tag=${PREV_TAG}" >> "$GITHUB_ENV" + echo "prev_found=true" >> "$GITHUB_OUTPUT" + echo "Previous: ${IMAGE_REPO}:${PREV_TAG}" + else + echo "No previous tag found for ${IMAGE_REPO}:${PREV_TAG} — treating as first release." + echo "prev_image=" >> "$GITHUB_ENV" + echo "prev_tag=" >> "$GITHUB_ENV" + echo "prev_found=false" >> "$GITHUB_OUTPUT" fi - echo "prev_image=${IMAGE_REPO}" >> "$GITHUB_ENV" - echo "prev_tag=${PREV_TAG}" >> "$GITHUB_ENV" - echo "prev_found=true" >> "$GITHUB_OUTPUT" - echo "Previous: ${IMAGE_REPO}:${PREV_TAG}" - - name: Generate RPM lists (new vs previous) per platform if: env.check_update != 0 From 00742102c1a3bbde85eae79d61cd68ea844e7259 Mon Sep 17 00:00:00 2001 From: ryosuke-nakayama Date: Wed, 8 Oct 2025 10:57:44 +0900 Subject: [PATCH 08/14] ci: switch
blocks to printf and always close after platform loop ci: switch
blocks to printf and always close after platform loop --- .github/workflows/build-test-push.yml | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index bf1aa36..c0a8791 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -444,12 +444,7 @@ jobs: # Collapse each platform's RPM diff into a
section - { - echo "" - echo "
" - echo "Platform: \`${plat}\`" - echo "" - } >> "${out}" + printf "\n
\nPlatform: \`%s\`\n\n" "$plat" >> "$ if [ -f "${old}" ]; then # Added / Removed sets based on names @@ -520,12 +515,9 @@ jobs: echo "" >> "${out}" any_section=true - { - echo "" - echo "
" - echo "" - } >> "${out}" fi + # Close the
section for this platform + printf "\n
\n" >> "${out}" done $any_section || echo "_No RPM changes detected (no comparable platforms found)._" >> "${out}" From 4872506c8b455bca088de7d029e930c645561c99 Mon Sep 17 00:00:00 2001 From: ryosuke-nakayama Date: Thu, 9 Oct 2025 11:42:17 +0900 Subject: [PATCH 09/14] feat: embed rechunker-style RPM metadata as compressed label in container images feat: embed rechunker-style RPM metadata as compressed label in container images --- .github/workflows/build-test-push.yml | 63 +++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index c0a8791..4d50a6d 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -526,6 +526,67 @@ jobs: mkdir -p rpm_diff_artifacts/${VMJ} mv "${out}" "rpm_diff_artifacts/${VMJ}/rpm_diff_${VMJ}_${IMT}.md" + - + name: Build rechunk-style label payload (gzip+base64) + if: env.check_update != 0 + id: build-rechunk-label + env: + PLATFORMS: ${{ env.platforms }} + VMJ: ${{ matrix.version_major }} + IMT: ${{ matrix.image_types }} + PREV_TAG: ${{ env.prev_tag }} + DTS: ${{ needs.init-data.outputs.date_time_stamp }} + LABEL_KEY: dev.hhd.rechunk.info + run: | + set -euo pipefail + base="rpm_lists/${VMJ}/${IMT}" + + # Create a base JSON + jq -n \ + --arg vmj "${VMJ}" \ + --arg imt "${IMT}" \ + --arg dts "${DTS}" \ + --arg prev "${PREV_TAG:-}" \ + '{ + version_major: $vmj, + image_type: $imt, + built_at: $dts, + prev_tag: ( $prev | select(length>0) // null ), + platforms: {} + }' > rechunk_${VMJ}_${IMT}.json + + # Add per-platform package lists and SHA256 + for p in ${PLATFORMS//,/ }; do + plat=$(echo "$p" | xargs) + key="${plat//\//_}" + new="${base}/new_${key}.txt" + [ -f "${new}" ] || continue + + sha=$(sha256sum "${new}" | awk '{print $1}') + # Convert TSV "nameversion-release" into JSON array + jq -Rs ' + split("\n") + | map(select(length>0) | split("\t") | {n: .[0], v: .[1]}) + ' "${new}" > pkgs_${key}.json + + tmp=$(mktemp) + jq --arg plat "${plat}" --arg sha "${sha}" --slurpfile pkgs pkgs_${key}.json \ + '.platforms[$plat] = { pkgs_sha256: $sha, pkgs: $pkgs[0] }' \ + "rechunk_${VMJ}_${IMT}.json" > "${tmp}" + mv "${tmp}" "rechunk_${VMJ}_${IMT}.json" + rm -f pkgs_${key}.json + done + + # Compress and encode JSON into a single-line lable payload + RCHUNK_INFO_LABEL=$(gzip -c "rechunk_${VMJ}_${IMT}.json" | base64 -w0) + echo "RCHUNK_INFO_LABEL=${RCHUNK_INFO_LABEL}" >> "$GITHUB_ENV" + echo "LABEL_KEY=${LABEL_KEY}" >> "$GITHUB_ENV" + + # Debug: show summary of the generated JSON without full package lists + echo "[Debug] schema/preview:" + jq 'del(.platforms[]?.pkgs) | .platforms |= with_entries(.value |= {pkgs_sha256:.pkgs_sha256, pkgs_count:0})' "rechunk_${VMJ}_${IMT}.json" || true + + - name: Upload RPM diff artifacts if: env.check_update != 0 @@ -546,6 +607,8 @@ jobs: platforms: ${{ env.platforms }} push: true tags: ${{ steps.meta.outputs.tags }} + labels: | + ${{ env.LABLE_KEY }}=${{ env.RCHUNK_INFO_LABEL }} - name: Prepare Mattermost message From 6ae0febc4baeb92c6a94d3075f19be39ecbf54b2 Mon Sep 17 00:00:00 2001 From: ryosuke-nakayama Date: Thu, 9 Oct 2025 20:03:29 +0900 Subject: [PATCH 10/14] ci: change LABEL_KEY to org.almalinux.sbom (replace dev.hhd.rechunk.info) --- .github/workflows/build-test-push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index 4d50a6d..317ceaf 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -536,7 +536,7 @@ jobs: IMT: ${{ matrix.image_types }} PREV_TAG: ${{ env.prev_tag }} DTS: ${{ needs.init-data.outputs.date_time_stamp }} - LABEL_KEY: dev.hhd.rechunk.info + LABEL_KEY: org.almalinux.sbom run: | set -euo pipefail base="rpm_lists/${VMJ}/${IMT}" From e9967c8dbfb91e82361205490363c9ab4006e53b Mon Sep 17 00:00:00 2001 From: ryosuke-nakayama Date: Thu, 9 Oct 2025 20:05:11 +0900 Subject: [PATCH 11/14] fix: correct label-related typos --- .github/workflows/build-test-push.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index 317ceaf..d90e7e0 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -577,7 +577,7 @@ jobs: rm -f pkgs_${key}.json done - # Compress and encode JSON into a single-line lable payload + # Compress and encode JSON into a single-line label payload RCHUNK_INFO_LABEL=$(gzip -c "rechunk_${VMJ}_${IMT}.json" | base64 -w0) echo "RCHUNK_INFO_LABEL=${RCHUNK_INFO_LABEL}" >> "$GITHUB_ENV" echo "LABEL_KEY=${LABEL_KEY}" >> "$GITHUB_ENV" @@ -608,7 +608,7 @@ jobs: push: true tags: ${{ steps.meta.outputs.tags }} labels: | - ${{ env.LABLE_KEY }}=${{ env.RCHUNK_INFO_LABEL }} + ${{ env.LABEL_KEY }}=${{ env.RCHUNK_INFO_LABEL }} - name: Prepare Mattermost message From ed58b53a4d78fc157d90a53c22b9eeb40ffa073a Mon Sep 17 00:00:00 2001 From: ryosuke-nakayama Date: Thu, 9 Oct 2025 21:00:42 +0900 Subject: [PATCH 12/14] ci: read previous RPM list from image label via crane instead of running old image ci: read previous RPM list from image label via crane instead of running old image ci: read previous RPM list from image label via crane instead of running old image ci: read previous RPM list from image label via crane instead of running old image ci: read previous RPM list from image label via crane instead of running old image --- .github/workflows/build-test-push.yml | 29 +++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index d90e7e0..483d7bd 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -371,6 +371,17 @@ jobs: echo "prev_found=false" >> "$GITHUB_OUTPUT" fi + - + name: Install crane + if: env.check_update != 0 + run: | + set -euo pipefail + ver=v0.20.2 + curl -sSL -o /tmp/crane.tgz \ + https://github.com/google/go-containerregistry/releases/download/${ver}/crane_Linux_x86_64.tar.gz + sudo tar -C /usr/local/bin -xzf /tmp/crane.tgz crane + crane version + - name: Generate RPM lists (new vs previous) per platform if: env.check_update != 0 @@ -402,11 +413,21 @@ jobs: # New image: refer by digest list_rpms "${IMG_NEW_DIGEST}" "${plat}" "rpm_lists/${VMJ}/${IMT}/new_${plat//\//_}.txt" - # Old image (if present): : + # Old image (if present): extract package list from label instead of running it if [ -n "${IMG_PREV:-}" ] && [ -n "${TAG_PREV:-}" ]; then - docker pull --platform "${plat}" "${IMG_PREV}:${TAG_PREV}" >/dev/null 2>&1 || true - list_rpms "${IMG_PREV}:${TAG_PREV}" "${plat}" "rpm_lists/${VMJ}/${IMT}/old_${plat//\//_}.txt" || \ - : # Continue even if platform not provided previously + echo "Fetching previous RPM list label..." + label_json=$(crane config "${IMG_PREV}:${TAG_PREV}" \ + | jq -r --arg key "${LABEL_KEY}" '.config.Labels[$key] // empty') + if [ -n "${label_json}" ]; then + val=$(jq -r --arg plat "${plat}" --argjson data "${label_json}" '($data[$plat]) // empty' "rpm_lists/${VMJ}/${IMT}/old_${plat//\//_}.txt" + else + echo "No previous entry for ${plat} in label ${LABEL_KEY}" + fi + else + echo "No previous label found for ${plat}" + fi fi done From 39a73ba85f1070cbff74b1574565bb11ab69b9c9 Mon Sep 17 00:00:00 2001 From: ryosuke-nakayama Date: Thu, 9 Oct 2025 21:30:27 +0900 Subject: [PATCH 13/14] refactor: store JSON label directly instead of gzip+base64 --- .github/workflows/build-test-push.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index 483d7bd..926f970 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -598,8 +598,8 @@ jobs: rm -f pkgs_${key}.json done - # Compress and encode JSON into a single-line label payload - RCHUNK_INFO_LABEL=$(gzip -c "rechunk_${VMJ}_${IMT}.json" | base64 -w0) + # Store the JSON directly in the label payload (no compression) + RCHUNK_INFO_LABEL=$(cat "rechunk_${VMJ}_${IMT}.json" | jq -c .) echo "RCHUNK_INFO_LABEL=${RCHUNK_INFO_LABEL}" >> "$GITHUB_ENV" echo "LABEL_KEY=${LABEL_KEY}" >> "$GITHUB_ENV" From 39bb7496668e22dc6976ee5136c7075cb9e1f4af Mon Sep 17 00:00:00 2001 From: ryosuke-nakayama Date: Fri, 10 Oct 2025 15:55:00 +0900 Subject: [PATCH 14/14] ci(registry,label,changelog): tweak testing registry, label fetch, and RPM diff output --- .github/workflows/build-test-push.yml | 56 +++++++++++---------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/.github/workflows/build-test-push.yml b/.github/workflows/build-test-push.yml index 926f970..9a287e6 100644 --- a/.github/workflows/build-test-push.yml +++ b/.github/workflows/build-test-push.yml @@ -376,9 +376,9 @@ jobs: if: env.check_update != 0 run: | set -euo pipefail - ver=v0.20.2 + ver=v0.20.6 curl -sSL -o /tmp/crane.tgz \ - https://github.com/google/go-containerregistry/releases/download/${ver}/crane_Linux_x86_64.tar.gz + https://github.com/google/go-containerregistry/releases/download/${ver}/go-containerregistry_Linux_x86_64.tar.gz sudo tar -C /usr/local/bin -xzf /tmp/crane.tgz crane crane version @@ -393,6 +393,7 @@ jobs: PLATFORMS: ${{ env.platforms }} VMJ: ${{ matrix.version_major }} IMT: ${{ matrix.image_types }} + LABEL_KEY: org.almalinux.sbom run: | set -euo pipefail mkdir -p rpm_lists/${VMJ}/${IMT} @@ -417,7 +418,7 @@ jobs: if [ -n "${IMG_PREV:-}" ] && [ -n "${TAG_PREV:-}" ]; then echo "Fetching previous RPM list label..." label_json=$(crane config "${IMG_PREV}:${TAG_PREV}" \ - | jq -r --arg key "${LABEL_KEY}" '.config.Labels[$key] // empty') + | jq -r --arg key "${LABEL_KEY:-org.almalinux.sbom}" '.config.Labels[$key] // empty') if [ -n "${label_json}" ]; then val=$(jq -r --arg plat "${plat}" --argjson data "${label_json}" '($data[$plat]) // empty' "${out}" if [ -n "${PREV_TAG}" ]; then - echo "" >> "${out}" - echo "- Compared to previous: \`${PREV_TAG}\`" >> "${out}" + echo "Changes since last version (${PREV_TAG}):" > "${out}" else - echo "" >> "${out}" - echo "- No previous dated tag found. Treating all as **Added**." >> "${out}" + echo "Changes since last version: (first release)" > "${out}" fi echo "" >> "${out}" @@ -463,20 +461,16 @@ jobs: [ ! -f "${new}" ] && continue - - # Collapse each platform's RPM diff into a
section - printf "\n
\nPlatform: \`%s\`\n\n" "$plat" >> "$ + { + echo "
" + printf "Platform: \`%s\`\n\n" "$plat" + } >> "${out}" if [ -f "${old}" ]; then # Added / Removed sets based on names comm -13 "${old%.txt}.names" "${new%.txt}.names" > added.tmp || true comm -23 "${old%.txt}.names" "${new%.txt}.names" > removed.tmp || true - # Updated: same name but different versions - join -j1 -t $'\t' -o 0,1.2,2.2 \ - <(join -t $'\t' -1 1 -2 1 <(cut -f1,2 "${old}" | sort) <(cut -f1,2 "${new}" | sort -k1,1) | sort -k1,1) \ - <(cut -f1,2 "${new}" | sort -k1,1) >/dev/null 2>&1 || true - # Recompute diffs and extract version changes updated_tmp=$(mktemp) awk -F'\t' 'NR==FNR{old[$1]=$2;next}{ if($1 in old && old[$1]!=$2){ print $1"\t"old[$1]" → "$2 } }' \ @@ -486,36 +480,32 @@ jobs: updc=$(wc -l < "${updated_tmp}" | tr -d ' ') delc=$(wc -l < removed.tmp | tr -d ' ') - printed=false + printed=false + echo "**Package Changes**" >> "${out}" + echo "" >> "${out}" - # Added - if [ "$addc" -gt 0 ]; then + if [ "$updc" -gt 0 ]; then { + echo "**Updated (${updc})**" echo "" - echo "**Added (${addc})**" - echo "" - awk '{print "- " $0}' added.tmp + awk -F'\t' '{printf "- %s: %s\n",$1,$2}' "${updated_tmp}" echo "" } >> "${out}" printed=true fi - # Updated - if [ "$updc" -gt 0 ]; then + if [ "$addc" -gt 0 ]; then { + echo "**Added (${addc})**" echo "" - echo "**Updated (${updc})**" - echo "" - awk -F'\t' '{printf "- %s: %s\n",$1,$2}' "${updated_tmp}" + awk -v OFS='\t' 'NR==FNR{ver[$1]=$2;next}{n=$0; if(n!=""){ printf "- %s: %s\n", n, ver[n] }}' "${new}" added.tmp echo "" } >> "${out}" printed=true fi - # Removed if [ "$delc" -gt 0 ]; then { - echo "" echo "**Removed (${delc})**" echo "" awk '{print "- " $0}' removed.tmp @@ -528,11 +518,11 @@ jobs: rm -f added.tmp removed.tmp "${updated_tmp}" else - # No previous list: treat all as Added + echo "**Package Changes**" >> "${out}" echo "" >> "${out}" - echo "**Added**" >> "${out}" + echo "_No previous version — initial publication (showing all as Added)_" >> "${out}" echo "" >> "${out}" - cut -f1 "${new}" | awk '{print "- " $0}' >> "${out}" + awk -F'\t' '{printf "- %s: %s\n",$1,$2}' "${new}" >> "${out}" echo "" >> "${out}" any_section=true @@ -541,7 +531,7 @@ jobs: printf "\n
\n" >> "${out}" done - $any_section || echo "_No RPM changes detected (no comparable platforms found)._" >> "${out}" + $any_section || echo "_No package changes detected._" >> "${out}" # Move artifacts into a readable path mkdir -p rpm_diff_artifacts/${VMJ}