diff --git a/base/comps/azurelinux-rpm-config/azurelinux-rpm-config.comp.toml b/base/comps/azurelinux-rpm-config/azurelinux-rpm-config.comp.toml index 3ab8ccb41d1..04f1ca2f9e7 100644 --- a/base/comps/azurelinux-rpm-config/azurelinux-rpm-config.comp.toml +++ b/base/comps/azurelinux-rpm-config/azurelinux-rpm-config.comp.toml @@ -13,7 +13,7 @@ overlays = [ { type = "spec-update-tag", tag = "Version", value = "1004" }, # WORKAROUND: manually bump the Release value for Stage2 bring-up. - { type = "spec-update-tag", tag = "Release", value = "2%{?dist}" }, + { type = "spec-update-tag", tag = "Release", value = "3%{?dist}" }, # Provide compatibility with Fedora's upstream version. { type = "spec-add-tag", tag = "Provides", value = "redhat-rpm-config = %{version}-%{release}" }, @@ -55,4 +55,42 @@ overlays = [ # Targeted adjustments in some macros files. { type = "file-search-replace", file = "macros.fedora-misc-srpm", regex = "/lib/swidtag/fedoraproject.org", replacement = "/lib/swidtag/microsoft.com" }, { type = "file-search-replace", file = "macros.fedora-misc-srpm", regex = "[Ff]edora", replacement = "azurelinux" }, + + # Add the %{?_generate_package_note_file} hook to %__spec_install_pre so + # that RPM_MODULE_VERSION (set by package-notes-srpm-macros via that hook) + # is exported during %install as well as %build and %check. Required for + # packages that invoke the linker from %install (e.g. avahi's libtool + # --relink for PIE libraries), since the package-notes GCC linker spec + # uses a strict %:getenv(RPM_MODULE_VERSION ) lookup that aborts the + # link if the env var is unset. The hook is already present in + # %__spec_build_pre and %__spec_check_pre in this same file -- this + # overlay just extends the pattern to %__spec_install_pre. Anchored on + # the 4-space-indented variant of the line (with no trailing space before + # the line-continuation backslash), which is unique to %__spec_install_pre + # -- the other two callers use 2-space indent and " \" at end of line. + { type = "file-search-replace", file = "macros", regex = ''' %\{\?_auto_set_build_flags:%\{set_build_flags\}\}\\''', replacement = ''' %{?_auto_set_build_flags:%{set_build_flags}}\ + %{?_generate_package_note_file}\''' }, + + # Self-test: the .note.package moduleVersion mechanism depends on the + # %_generate_package_note_file hook remaining expanded in all three + # %__spec_*_pre macros (build / check / install). package-notes-srpm-macros + # redefines that hook to export RPM_MODULE_VERSION; if a future rebase of + # redhat-rpm-config drops these expansions, the export silently stops and + # every downstream binary loses its moduleVersion (and %install-time + # relinks such as avahi's libtool --relink would break on the strict + # %:getenv lookup). Fail this build loudly instead of regressing silently. + # + # NB: we grep for the bare macro NAME '_generate_package_note_file', not the + # '%{?_generate_package_note_file}' expansion -- RPM would expand the latter + # in the spec before the shell runs, and the macro is undefined during this + # package's own build (it ships in package-notes-srpm-macros), so the + # pattern would collapse to empty and match everything. + { type = "spec-append-lines", section = "%install", lines = [ + "# Guard: ensure the package-notes moduleVersion hook is still wired in (see comp overlay).", + "note_hooks=$(grep -c '_generate_package_note_file' macros 2>/dev/null || echo 0)", + "if [ \"$note_hooks\" -lt 3 ]; then", + " echo \"ERROR: macros has $note_hooks '_generate_package_note_file' hook expansion(s), expected >= 3 (build/check/install _pre). A redhat-rpm-config rebase may have dropped the .note.package moduleVersion hook.\" >&2", + " exit 1", + "fi", + ] }, ] diff --git a/base/comps/components.toml b/base/comps/components.toml index 7bda856a6a0..60e20deb897 100644 --- a/base/comps/components.toml +++ b/base/comps/components.toml @@ -2298,7 +2298,6 @@ includes = ["**/*.comp.toml", "component-check-disablement.toml", "component-min [components.osinfo-db] [components.osinfo-db-tools] [components.p11-kit] -[components.package-notes] [components.pam] [components.pam_wrapper] [components.pandoc] diff --git a/base/comps/package-notes/package-notes.comp.toml b/base/comps/package-notes/package-notes.comp.toml new file mode 100644 index 00000000000..929446bb0b3 --- /dev/null +++ b/base/comps/package-notes/package-notes.comp.toml @@ -0,0 +1,83 @@ +[components.package-notes] + +# Add structured 'os' and 'osVersion' fields to the .note.package ELF section +# emitted into every binary built with these LDFLAGS, alongside a parsed +# 'moduleVersion'. These give SBOM and crash-analysis tools a parsed view of +# the build's OS identity and package version without having to crack the CPE +# string. +# +# osCpe is hardcoded to the canonical Azure Linux CPE 2.3 string rather than +# read from /usr/lib/system-release-cpe in %build. During Stage 1 the buildroot +# is populated from the Fedora seed package set, so that file reports the +# Fedora CPE (e.g. cpe:/o:fedoraproject:fedora:43) and would bake the wrong +# identity into every binary. Hardcoding guarantees correct metadata +# regardless of which package set seeded the buildroot. +# +# @OSCPE@, @OS@ and @OSVERSION@ are placeholders added to the linker-script +# template by the overlay below; we substitute them in %build. os/osVersion +# mirror /etc/os-release's ID/VERSION_ID (using the underscored 'azure_linux' +# CPE project name), and osCpe is the matching CPE 2.3 string. +[[components.package-notes.overlays]] +description = "Substitute @OSCPE@/@OS@/@OSVERSION@ placeholders in the .note.package linker spec, then self-test that the os/osVersion/moduleVersion overlays applied" +type = "spec-search-replace" +section = "%build" +regex = 'sed "s\|@OSCPE@\|\$\(cat /usr/lib/system-release-cpe\)\|" %\{SOURCE0\} >redhat-package-notes' +replacement = '''sed -e "s|@OSCPE@|cpe:2.3:o:microsoft:azure_linux:4.0:*:*:*:*:*:*:*|" -e "s|@OS@|azure_linux|" -e "s|@OSVERSION@|4.0|" %{SOURCE0} >redhat-package-notes + +# Self-test: fail the build loudly if these overlays did not inject the +# expected .note.package fields into the generated linker spec, rather than +# silently shipping binaries with missing OS / moduleVersion metadata. The +# bare "os" field is added by the same overlay as osVersion and moduleVersion, +# so verifying these three covers it. +for field in osCpe osVersion moduleVersion; do + grep -qF "$field" redhat-package-notes || { echo "ERROR: generated redhat-package-notes is missing the '$field' field -- package-notes overlay regression" >&2; exit 1; } +done +# moduleVersion's value comes from RPM_MODULE_VERSION, exported by the +# %_generate_package_note_file hook in macros.package-notes-srpm. If a future +# rebase reverts that macro to the upstream %{nil}, moduleVersion would +# silently become empty in every downstream binary -- fail now instead. +grep -q RPM_MODULE_VERSION %{SOURCE1} || { echo "ERROR: macros.package-notes-srpm no longer exports RPM_MODULE_VERSION -- the _generate_package_note_file hook was removed" >&2; exit 1; }''' + +# Compute a 4-part RPM_MODULE_VERSION from RPM_PACKAGE_VERSION and export it +# into every %build shell. We hook into the %_generate_package_note_file +# macro, which the Azure Linux RPM macros (/usr/lib/rpm/azurelinux/macros) +# already expand inside %__spec_build_pre. The package-notes-srpm-macros +# file ships this macro as %{nil} by default; we redefine it to our export. +# (Overriding %__spec_build_pre directly does not work -- azurelinux/macros +# loads later in the macro path and clobbers any earlier definition.) +# Per field: truncate at the first non-digit (so a tilde-prerelease or git +# suffix is stripped, not folded into the numeric value) and zero-pad +# missing trailing fields: +# 1.6.1 -> 1.6.1.0 +# 1.2.3~rc1 -> 1.2.3.0 +# 0.9~rc2 -> 0.9.0.0 +# 2024.05.29 -> 2024.5.29.0 +# 0^20260120.g386b5f5 -> 20260120.0.0.0 (^ truncates field 1 to "0", +# g... truncates field 2 to "") +# The value is consumed by the linker-script template (overlay below) via +# %:getenv(RPM_MODULE_VERSION ) and emitted as the moduleVersion key in +# .note.package. +[[components.package-notes.overlays]] +description = "Hook %_generate_package_note_file to export 4-part RPM_MODULE_VERSION for every package build, consumed by the .note.package moduleVersion field" +type = "file-search-replace" +file = "macros.package-notes-srpm" +regex = '%_generate_package_note_file %\{nil\}' +replacement = '''%_generate_package_note_file export RPM_MODULE_VERSION=$(echo "${RPM_PACKAGE_VERSION:-0}" | awk -F. '{for(i=1;i<=4;i++){v=$i; sub(/[^0-9].*/,"",v); a[i]=v+0}; print a[1]"."a[2]"."a[3]"."a[4]}')''' + +# Add the os, osVersion, and moduleVersion keys to the JSON payload emitted +# into .note.package. The template is a GCC spec file fragment; embedded +# quotes are escaped as \". Adding one more %:getenv(...) requires bumping the +# trailing ')' count by one (from 4 to 5) to keep the macro nesting balanced. +# +# Note: this introduces a runtime dependency on RPM_MODULE_VERSION being +# exported during every rpmbuild phase that may invoke the linker. The +# azurelinux-rpm-config component carries a companion overlay that adds +# %{?_generate_package_note_file} to %__spec_install_pre -- without that, +# packages that relink during %install (e.g. avahi's libtool --relink for +# PIE libraries) fail because GCC's %:getenv() is a strict lookup. +[[components.package-notes.overlays]] +description = "Add os, osVersion, and moduleVersion keys to the .note.package JSON payload" +type = "file-search-replace" +file = "redhat-package-notes.in" +regex = '\\"osCpe\\":\\"@OSCPE@\\"\}\)\)\)\)\}' +replacement = '\"osCpe\":\"@OSCPE@\",\"os\":\"@OS@\",\"osVersion\":\"@OSVERSION@\",\"moduleVersion\":\"%:getenv(RPM_MODULE_VERSION \"})))))}' diff --git a/locks/azurelinux-rpm-config.lock b/locks/azurelinux-rpm-config.lock index 02db39c7bfb..994f1ce5b3b 100644 --- a/locks/azurelinux-rpm-config.lock +++ b/locks/azurelinux-rpm-config.lock @@ -2,5 +2,5 @@ version = 1 import-commit = '6e4185bb29f2ca019b36bec8006e7ad60e58cade' upstream-commit = '6e4185bb29f2ca019b36bec8006e7ad60e58cade' -input-fingerprint = 'sha256:cb133fba511f946ad698cc0b1eb4487223b9710e6253522195d7f277c77d2649' +input-fingerprint = 'sha256:8a286da0582b041b54325aabefbadc74901af8d198f10239946f0bbb395c5b6e' resolution-input-hash = 'sha256:ed1c0158b0942459a3c1a7f3db4f08a88242ce03d0f325b50e1c411f48c8c514' diff --git a/locks/package-notes.lock b/locks/package-notes.lock index d1153b50a5c..da5d743bc13 100644 --- a/locks/package-notes.lock +++ b/locks/package-notes.lock @@ -2,5 +2,5 @@ version = 1 import-commit = 'f206ceb68e37296a3255df75e9061434bb7525a0' upstream-commit = 'f206ceb68e37296a3255df75e9061434bb7525a0' -input-fingerprint = 'sha256:74df4d501e6329669c64bdff6c349438f8bc68f1d674541b3897284f6e72e7db' +input-fingerprint = 'sha256:e84541abd3b5d334b6fc6aa53b35c32efc68b1623bbcdb1334a0aaa942bed7a3' resolution-input-hash = 'sha256:466421704711c4fd3c71f0b2ed715a0e61d49e3e26f3a2637fee755795849c8e' diff --git a/specs/a/azurelinux-rpm-config/azurelinux-rpm-config.spec b/specs/a/azurelinux-rpm-config/azurelinux-rpm-config.spec index 89d7a5100a2..1c4af3a6660 100644 --- a/specs/a/azurelinux-rpm-config/azurelinux-rpm-config.spec +++ b/specs/a/azurelinux-rpm-config/azurelinux-rpm-config.spec @@ -17,7 +17,7 @@ Name: azurelinux-rpm-config # the older branch. When the branch diverges, bump the Version to the Fedora # release number. Version: 1004 -Release: 3%{?dist} +Release: 5%{?dist} # config.guess, config.sub are GPL-3.0-or-later WITH Autoconf-exception-generic License: GPL-1.0-or-later AND GPL-2.0-or-later AND GPL-3.0-or-later WITH Autoconf-exception-generic URL: https://aka.ms/azurelinux @@ -222,6 +222,12 @@ install -p -m 644 -t %{buildroot}%{_rpmluadir}/fedora common.lua # Hence it is necessary to trigger on both gcc and gcc-plugin-annobin. ln -sf fedora %{buildroot}%{_rpmluadir}/azurelinux +# Guard: ensure the package-notes moduleVersion hook is still wired in (see comp overlay). +note_hooks=$(grep -c '_generate_package_note_file' macros 2>/dev/null || echo 0) +if [ "$note_hooks" -lt 3 ]; then + echo "ERROR: macros has $note_hooks '_generate_package_note_file' hook expansion(s), expected >= 3 (build/check/install _pre). A redhat-rpm-config rebase may have dropped the .note.package moduleVersion hook." >&2 + exit 1 +fi %triggerin -- annobin-plugin-gcc gcc-plugin-annobin gcc %{rrcdir}/redhat-annobin-plugin-select.sh %end diff --git a/specs/a/azurelinux-rpm-config/macros b/specs/a/azurelinux-rpm-config/macros index 8415f7e59d5..151713f2bed 100644 --- a/specs/a/azurelinux-rpm-config/macros +++ b/specs/a/azurelinux-rpm-config/macros @@ -251,6 +251,7 @@ for k,_ in pairs(stripped_flags) do print(k .. " ") end mkdir -p "`dirname "$RPM_BUILD_ROOT"`"\ mkdir "$RPM_BUILD_ROOT"\ %{?_auto_set_build_flags:%{set_build_flags}}\ + %{?_generate_package_note_file}\ %{nil} #--------------------------------------------------------------------- diff --git a/specs/p/package-notes/macros.package-notes-srpm b/specs/p/package-notes/macros.package-notes-srpm index 37248973ad0..c9d72412f4f 100644 --- a/specs/p/package-notes/macros.package-notes-srpm +++ b/specs/p/package-notes/macros.package-notes-srpm @@ -16,7 +16,7 @@ # These are defined for backwards compatibility. Do not use. %_package_note_file 1 -%_generate_package_note_file %{nil} +%_generate_package_note_file export RPM_MODULE_VERSION=$(echo "${RPM_PACKAGE_VERSION:-0}" | awk -F. '{for(i=1;i<=4;i++){v=$i; sub(/[^0-9].*/,"",v); a[i]=v+0}; print a[1]"."a[2]"."a[3]"."a[4]}') # Overall status: 1 if looks like we can insert the note, 0 otherwise # Unfortunately "clang" does not support specs files so the note insertion is disabled when using it. diff --git a/specs/p/package-notes/package-notes.spec b/specs/p/package-notes/package-notes.spec index 7cc1bb9260f..2582d9914dd 100644 --- a/specs/p/package-notes/package-notes.spec +++ b/specs/p/package-notes/package-notes.spec @@ -2,7 +2,7 @@ ## (rpmautospec version 0.8.3) ## RPMAUTOSPEC: autorelease, autochangelog %define autorelease(e:s:pb:n) %{?-p:0.}%{lua: - release_number = 16; + release_number = 17; base_release_number = tonumber(rpm.expand("%{?-b*}%{!?-b:1}")); print(release_number + base_release_number - 1); }%{?-e:.%{-e*}}%{?-s:.%{-s*}}%{!?-n:%{?dist}} @@ -45,7 +45,21 @@ describes the package the binary was built for via a compiler spec file. # nothing to do %build -sed "s|@OSCPE@|$(cat /usr/lib/system-release-cpe)|" %{SOURCE0} >redhat-package-notes +sed -e "s|@OSCPE@|cpe:2.3:o:microsoft:azure_linux:4.0:*:*:*:*:*:*:*|" -e "s|@OS@|azure_linux|" -e "s|@OSVERSION@|4.0|" %{SOURCE0} >redhat-package-notes + +# Self-test: fail the build loudly if these overlays did not inject the +# expected .note.package fields into the generated linker spec, rather than +# silently shipping binaries with missing OS / moduleVersion metadata. The +# bare "os" field is added by the same overlay as osVersion and moduleVersion, +# so verifying these three covers it. +for field in osCpe osVersion moduleVersion; do + grep -qF "$field" redhat-package-notes || { echo "ERROR: generated redhat-package-notes is missing the '$field' field -- package-notes overlay regression" >&2; exit 1; } +done +# moduleVersion's value comes from RPM_MODULE_VERSION, exported by the +# %_generate_package_note_file hook in macros.package-notes-srpm. If a future +# rebase reverts that macro to the upstream %{nil}, moduleVersion would +# silently become empty in every downstream binary -- fail now instead. +grep -q RPM_MODULE_VERSION %{SOURCE1} || { echo "ERROR: macros.package-notes-srpm no longer exports RPM_MODULE_VERSION -- the _generate_package_note_file hook was removed" >&2; exit 1; } %install install -Dt %{buildroot}%{_rpmconfigdir}/redhat/ redhat-package-notes @@ -57,6 +71,10 @@ install -m0644 -Dt %{buildroot}%{_rpmmacrodir}/ %{SOURCE1} %changelog ## START: Generated by rpmautospec +* Thu Jun 11 2026 Andrew Phelps - 0.5-17 +- feat(package-notes): embed os, osVersion, moduleVersion and fixed osCpe + in .note.package + * Thu Apr 30 2026 Daniel McIlvaney - 0.5-16 - feat: introduce deterministic commit resolution via Azure Linux lock file diff --git a/specs/p/package-notes/redhat-package-notes.in b/specs/p/package-notes/redhat-package-notes.in index 1297ae52618..e2db9b75c4c 100644 --- a/specs/p/package-notes/redhat-package-notes.in +++ b/specs/p/package-notes/redhat-package-notes.in @@ -1,2 +1,2 @@ *link: -+ %{!r:--package-metadata={\"type\":\"rpm\",\"name\":\"%:getenv(RPM_PACKAGE_NAME \",\"version\":\"%:getenv(RPM_PACKAGE_VERSION -%:getenv(RPM_PACKAGE_RELEASE \",\"architecture\":\"%:getenv(RPM_ARCH \",\"osCpe\":\"@OSCPE@\"}))))} ++ %{!r:--package-metadata={\"type\":\"rpm\",\"name\":\"%:getenv(RPM_PACKAGE_NAME \",\"version\":\"%:getenv(RPM_PACKAGE_VERSION -%:getenv(RPM_PACKAGE_RELEASE \",\"architecture\":\"%:getenv(RPM_ARCH \",\"osCpe\":\"@OSCPE@\",\"os\":\"@OS@\",\"osVersion\":\"@OSVERSION@\",\"moduleVersion\":\"%:getenv(RPM_MODULE_VERSION \"})))))}