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 @@ -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}" },
Expand Down Expand Up @@ -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",
] },
]
1 change: 0 additions & 1 deletion base/comps/components.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
83 changes: 83 additions & 0 deletions base/comps/package-notes/package-notes.comp.toml
Original file line number Diff line number Diff line change
@@ -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 \"})))))}'
2 changes: 1 addition & 1 deletion locks/azurelinux-rpm-config.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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'
2 changes: 1 addition & 1 deletion locks/package-notes.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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'
8 changes: 7 additions & 1 deletion specs/a/azurelinux-rpm-config/azurelinux-rpm-config.spec
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions specs/a/azurelinux-rpm-config/macros
Original file line number Diff line number Diff line change
Expand Up @@ -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}

#---------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion specs/p/package-notes/macros.package-notes-srpm
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
22 changes: 20 additions & 2 deletions specs/p/package-notes/package-notes.spec
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand Down Expand Up @@ -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
Expand All @@ -57,6 +71,10 @@ install -m0644 -Dt %{buildroot}%{_rpmmacrodir}/ %{SOURCE1}

%changelog
## START: Generated by rpmautospec
* Thu Jun 11 2026 Andrew Phelps <anphel@microsoft.com> - 0.5-17
- feat(package-notes): embed os, osVersion, moduleVersion and fixed osCpe
in .note.package

* Thu Apr 30 2026 Daniel McIlvaney <damcilva@microsoft.com> - 0.5-16
- feat: introduce deterministic commit resolution via Azure Linux lock file

Expand Down
2 changes: 1 addition & 1 deletion specs/p/package-notes/redhat-package-notes.in
Original file line number Diff line number Diff line change
@@ -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 \"})))))}
Loading