mirror of
https://github.com/jmcorgan/fips.git
synced 2026-10-05 19:18:25 +00:00
Version release-candidate .ipk packages so opkg sorts them below the release
A release candidate is tagged vX.Y.Z-rcN, and the OpenWrt workflow wrote that tag unchanged as the .ipk control Version. opkg compares versions with dpkg's algorithm and reads the text after the last '-' as a revision, so v0.5.3-rc1 sorted above v0.5.3 and a router that had installed the candidate was never upgraded by the release. The "Derive package version" step now also emits ipk_version, which maps a candidate tag's -alphaN/-betaN/-preN/-rcN suffix to '~' (the same mapping the .deb uses), giving v0.5.3~rc1. The leading 'v' is kept: every released .ipk carries it, and under opkg 0.5.4 would sort below v0.5.3, so dropping it would stop future releases upgrading. Release tags and branch builds are unchanged. build-ipk.sh takes the control Version from IPK_VERSION, falling back to PKG_VERSION, and still names the file from PKG_VERSION so the asset name keeps the tag's -rcN (a GitHub release renames assets with '~'). check-package-versions.sh gains an .ipk section that runs the step's text for a release tag, an rc and a beta candidate tag and a branch, checks the ordering against v0.5.2, v0.5.3 and v0.5.4 and of the beta below the rc, and checks the job output and build step wiring. package-test.sh builds the .ipk with IPK_VERSION removed from the build's environment and with it set, and reads the control Version back. It removes the first package before the second build and records a missing file as a failed check, so a build-ipk.sh that named its file from IPK_VERSION cannot pass by reading the first build's package, and its exit trap also removes a package whose name carries the '~' form.
This commit is contained in:
@@ -25,6 +25,7 @@ jobs:
|
||||
outputs:
|
||||
package_version: ${{ steps.version.outputs.package_version }}
|
||||
apk_version: ${{ steps.version.outputs.apk_version }}
|
||||
ipk_version: ${{ steps.version.outputs.ipk_version }}
|
||||
release_channel: ${{ steps.channel.outputs.release_channel }}
|
||||
steps:
|
||||
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
||||
@@ -41,15 +42,28 @@ jobs:
|
||||
# in the .apk metadata. apk_version is built directly from the same
|
||||
# structured inputs (tag, or commit height) — no reparse of the
|
||||
# flattened package_version. See packaging/openwrt-apk/apk-version.sh.
|
||||
#
|
||||
# ipk_version is the .ipk control Version. opkg compares versions as
|
||||
# dpkg does, so it reads a tag's vX.Y.Z-rcN as revision rcN and sorts
|
||||
# it above the release; vX.Y.Z~rcN sorts below it. git refuses '~' in
|
||||
# a ref name, so the tag carries '-' and this maps it. The leading 'v'
|
||||
# stays: every released .ipk carries it, and under opkg 0.5.4 sorts
|
||||
# below v0.5.3. testing/check-package-versions.sh runs this step's text.
|
||||
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
|
||||
echo "package_version=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "apk_version=$(sh packaging/openwrt-apk/apk-version.sh tag "${GITHUB_REF_NAME}")" >> "$GITHUB_OUTPUT"
|
||||
IPK_VERSION="${GITHUB_REF_NAME}"
|
||||
if [[ "$GITHUB_REF_NAME" =~ ^(v[0-9]+\.[0-9]+\.[0-9]+)-((alpha|beta|pre|rc)[0-9]*)$ ]]; then
|
||||
IPK_VERSION="${BASH_REMATCH[1]}~${BASH_REMATCH[2]}"
|
||||
fi
|
||||
echo "ipk_version=${IPK_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
BRANCH=$(echo "$GITHUB_REF_NAME" | sed 's|/|-|g')
|
||||
HEIGHT=$(git rev-list --count HEAD)
|
||||
HASH=$(git rev-parse --short HEAD)
|
||||
echo "package_version=${BRANCH}.${HEIGHT}.${HASH}" >> "$GITHUB_OUTPUT"
|
||||
echo "apk_version=$(sh packaging/openwrt-apk/apk-version.sh dev "${HEIGHT}")" >> "$GITHUB_OUTPUT"
|
||||
echo "ipk_version=${BRANCH}.${HEIGHT}.${HASH}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Determine release channel
|
||||
@@ -332,6 +346,7 @@ jobs:
|
||||
- name: Build .ipk
|
||||
env:
|
||||
PKG_VERSION: ${{ needs.determine-versioning.outputs.package_version }}
|
||||
IPK_VERSION: ${{ needs.determine-versioning.outputs.ipk_version }}
|
||||
run: ./packaging/openwrt-ipk/build-ipk.sh --arch ${{ matrix.build_arch }} --bin-dir "$GITHUB_WORKSPACE/bins"
|
||||
|
||||
- name: Install shellcheck (if missing)
|
||||
|
||||
@@ -14,7 +14,13 @@
|
||||
# arm 32-bit ARM routers (Cortex-A7)
|
||||
# x86_64 x86 routers / VMs
|
||||
#
|
||||
# Output: dist/fips_<version>_<openwrt-arch>.ipk
|
||||
# Output: dist/fips_<PKG_VERSION>_<openwrt-arch>.ipk
|
||||
#
|
||||
# Environment:
|
||||
# PKG_VERSION label for the file name (default: git describe)
|
||||
# IPK_VERSION control file Version (default: PKG_VERSION). CI passes a
|
||||
# release candidate as vX.Y.Z~rcN, which opkg sorts below the
|
||||
# release, while PKG_VERSION keeps the tag's vX.Y.Z-rcN.
|
||||
#
|
||||
# Prerequisites:
|
||||
# cargo install cargo-zigbuild
|
||||
@@ -86,8 +92,13 @@ DIST_DIR="$PROJECT_ROOT/dist"
|
||||
|
||||
PKG_NAME="fips"
|
||||
PKG_VERSION="${PKG_VERSION:-$(cd "$PROJECT_ROOT" && git describe --tags --always --dirty 2>/dev/null || echo "0.1.0")}"
|
||||
IPK_VERSION="${IPK_VERSION:-$PKG_VERSION}"
|
||||
|
||||
echo "==> Building $PKG_NAME $PKG_VERSION for $OPENWRT_ARCH ($RUST_TARGET)"
|
||||
if [ "$IPK_VERSION" = "$PKG_VERSION" ]; then
|
||||
echo "==> Building $PKG_NAME $PKG_VERSION for $OPENWRT_ARCH ($RUST_TARGET)"
|
||||
else
|
||||
echo "==> Building $PKG_NAME $PKG_VERSION (control Version $IPK_VERSION) for $OPENWRT_ARCH ($RUST_TARGET)"
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 1. Obtain binaries
|
||||
@@ -192,7 +203,7 @@ PKG_SIZE=$(du -sk "$DATA_DIR" | cut -f1)
|
||||
|
||||
cat > "$CONTROL_DIR/control" <<EOF
|
||||
Package: $PKG_NAME
|
||||
Version: $PKG_VERSION
|
||||
Version: $IPK_VERSION
|
||||
Architecture: $OPENWRT_ARCH
|
||||
Maintainer: FIPS Network
|
||||
Section: net
|
||||
|
||||
@@ -23,6 +23,26 @@
|
||||
# would leave checksums-linux.txt naming a file the release does not have).
|
||||
# Those three are read from the text, not executed.
|
||||
#
|
||||
# OpenWrt's opkg compares versions with dpkg's algorithm (libopkg/pkg.c
|
||||
# verrevcmp in openwrt/opkg-lede, read at 80503d94) and splits a revision at
|
||||
# the last '-', so the .ipk has the same defect. package-openwrt.yml's "Derive
|
||||
# package version" step maps the tag for the .ipk control Version, keeping the
|
||||
# leading 'v' every released .ipk carries: under opkg, 0.5.4 sorts below
|
||||
# v0.5.3, so dropping it would stop releases upgrading. dpkg stands in for
|
||||
# opkg as the ordering oracle; it warns about the 'v' and still compares.
|
||||
#
|
||||
# ipk cases:
|
||||
# refs/tags/v0.5.3 package_version and ipk_version v0.5.3
|
||||
# refs/tags/v0.5.3-rc1 ipk_version v0.5.3~rc1, package_version (the file
|
||||
# label) v0.5.3-rc1, and v0.5.2 < v0.5.3~rc1 < v0.5.3
|
||||
# < v0.5.4
|
||||
# refs/tags/v0.5.3-beta1 ipk_version v0.5.3~beta1, which sorts between
|
||||
# v0.5.2 and v0.5.3~rc1
|
||||
# refs/heads/maint ipk_version equals package_version
|
||||
# and the wiring: the job declares ipk_version, and the "Build .ipk" step
|
||||
# passes it as IPK_VERSION. build-ipk.sh's use of IPK_VERSION is executed by
|
||||
# testing/openwrt/package-test.sh.
|
||||
#
|
||||
# Exit 0 = clean. Exit 1 = a case or a wiring check failed, including a
|
||||
# derivation that ran but did not write a declared output. Exit 2 = the check
|
||||
# could not run (no dpkg, no PyYAML, a step not found, the derivation failed,
|
||||
@@ -200,6 +220,46 @@ expect_text "$WORK/deb.build.sh" \
|
||||
expect_text "$WORK/deb.build.sh" "tr '~' '-'" \
|
||||
"the .deb build renames a '~' in the package file name"
|
||||
|
||||
# ── OpenWrt .ipk, package-openwrt.yml ─────────────────────────────────────
|
||||
extract ipk package-openwrt.yml determine-versioning "Derive package version" \
|
||||
build "Build .ipk"
|
||||
|
||||
echo "ipk: release tag refs/tags/v0.5.3"
|
||||
derive ipk refs/tags/v0.5.3 package_version ipk_version
|
||||
expect_eq "package_version" "${OUT[package_version]}" "v0.5.3"
|
||||
expect_eq "ipk_version" "${OUT[ipk_version]}" "v0.5.3"
|
||||
|
||||
echo "ipk: candidate tag refs/tags/v0.5.3-rc1"
|
||||
derive ipk refs/tags/v0.5.3-rc1 package_version ipk_version
|
||||
expect_eq "package_version" "${OUT[package_version]}" "v0.5.3-rc1"
|
||||
expect_eq "ipk_version" "${OUT[ipk_version]}" "v0.5.3~rc1"
|
||||
expect_order "${OUT[ipk_version]}" lt v0.5.3
|
||||
expect_order "${OUT[ipk_version]}" gt v0.5.2
|
||||
expect_order v0.5.4 gt "${OUT[ipk_version]}"
|
||||
RC1_IPK="${OUT[ipk_version]}"
|
||||
|
||||
echo "ipk: candidate tag refs/tags/v0.5.3-beta1"
|
||||
derive ipk refs/tags/v0.5.3-beta1 package_version ipk_version
|
||||
expect_eq "package_version" "${OUT[package_version]}" "v0.5.3-beta1"
|
||||
expect_eq "ipk_version" "${OUT[ipk_version]}" "v0.5.3~beta1"
|
||||
expect_order "${OUT[ipk_version]}" lt "$RC1_IPK"
|
||||
expect_order "${OUT[ipk_version]}" gt v0.5.2
|
||||
|
||||
echo "ipk: branch refs/heads/maint"
|
||||
derive ipk refs/heads/maint package_version ipk_version
|
||||
expect_eq "package_version" "${OUT[package_version]}" "maint.${HEIGHT}.${HASH}"
|
||||
expect_eq "ipk_version" "${OUT[ipk_version]}" "${OUT[package_version]}"
|
||||
|
||||
echo "ipk: wiring"
|
||||
# shellcheck disable=SC2016 # the ${{ }} expressions are workflow text, not shell
|
||||
expect_text -x "$WORK/ipk.outputs" \
|
||||
'ipk_version=${{ steps.version.outputs.ipk_version }}' \
|
||||
"determine-versioning declares ipk_version from the derivation step"
|
||||
# shellcheck disable=SC2016
|
||||
expect_text -x "$WORK/ipk.build.sh" \
|
||||
'IPK_VERSION: ${{ needs.determine-versioning.outputs.ipk_version }}' \
|
||||
"the .ipk build passes ipk_version as IPK_VERSION"
|
||||
|
||||
echo
|
||||
if [ "$FAILED" -eq 0 ]; then
|
||||
echo "check-package-versions: all checks passed"
|
||||
|
||||
@@ -47,7 +47,7 @@ fi
|
||||
PKG_VERSION="pkgtest.$$"
|
||||
TMP="$(mktemp -d)" || { echo "package-test: mktemp failed" >&2; exit 2; }
|
||||
|
||||
trap 'rm -rf "$TMP"; rm -f "$PROJECT_ROOT/dist/fips_${PKG_VERSION}_"*' EXIT
|
||||
trap 'rm -rf "$TMP"; rm -f "$PROJECT_ROOT/dist/fips_${PKG_VERSION}"[_~]*' EXIT
|
||||
|
||||
harness_fail() {
|
||||
echo "package-test: $*" >&2
|
||||
@@ -227,7 +227,9 @@ done
|
||||
# ── Build the .ipk ──────────────────────────────────────────────────────────
|
||||
|
||||
echo "==> build-ipk.sh"
|
||||
if ! PKG_VERSION="$PKG_VERSION" \
|
||||
# The first build is V1's "IPK_VERSION unset" case, so it must not inherit an
|
||||
# IPK_VERSION from the caller's environment.
|
||||
if ! env -u IPK_VERSION PKG_VERSION="$PKG_VERSION" \
|
||||
bash "$PROJECT_ROOT/packaging/openwrt-ipk/build-ipk.sh" --arch x86_64 --bin-dir "$BINS" \
|
||||
> "$TMP/build-ipk.log" 2>&1; then
|
||||
cat "$TMP/build-ipk.log" >&2
|
||||
@@ -240,6 +242,44 @@ tar -xzf "$IPK" -O ./data.tar.gz | tar -tzf - > "$TMP/ipk-data" \
|
||||
tar -xzf "$IPK" -O ./control.tar.gz | tar -tzf - > "$TMP/ipk-control" \
|
||||
|| harness_fail "cannot list control.tar.gz in $IPK"
|
||||
|
||||
# Print the Version field of an .ipk's control file.
|
||||
ipk_version() {
|
||||
tar -xzf "$1" -O ./control.tar.gz | tar -xzOf - ./control \
|
||||
| sed -n 's/^Version: //p'
|
||||
}
|
||||
|
||||
# ── V1 and V2. The control Version comes from IPK_VERSION ───────────────────
|
||||
# The workflow passes a candidate's opkg-sortable version (vX.Y.Z~rcN) as
|
||||
# IPK_VERSION and keeps the tag's form in PKG_VERSION for the file name, since
|
||||
# a GitHub release renames an asset with '~' in its name. Unset, IPK_VERSION
|
||||
# falls back to PKG_VERSION, as a local build expects.
|
||||
got="$(ipk_version "$IPK")" || harness_fail "cannot read the control file in $IPK"
|
||||
if [[ "$got" == "$PKG_VERSION" ]]; then
|
||||
ok "V1 with IPK_VERSION unset the control Version is PKG_VERSION ($got)"
|
||||
else
|
||||
bad "V1 with IPK_VERSION unset the control Version is '$got', want $PKG_VERSION"
|
||||
fi
|
||||
# The first build's package is removed so a second build that names its file
|
||||
# from anything but PKG_VERSION cannot be passed by reading the stale one.
|
||||
RC_VERSION="${PKG_VERSION}~rc1"
|
||||
rm -f "$IPK" || harness_fail "cannot remove $IPK before the second build"
|
||||
if ! PKG_VERSION="$PKG_VERSION" IPK_VERSION="$RC_VERSION" \
|
||||
bash "$PROJECT_ROOT/packaging/openwrt-ipk/build-ipk.sh" --arch x86_64 --bin-dir "$BINS" \
|
||||
> "$TMP/build-ipk-rc.log" 2>&1; then
|
||||
cat "$TMP/build-ipk-rc.log" >&2
|
||||
harness_fail "build-ipk.sh failed with IPK_VERSION set"
|
||||
fi
|
||||
if [[ ! -f "$IPK" ]]; then
|
||||
bad "V2 with IPK_VERSION set build-ipk.sh wrote no $IPK; the file name must come from PKG_VERSION"
|
||||
else
|
||||
got="$(ipk_version "$IPK")" || harness_fail "cannot read the control file in $IPK"
|
||||
if [[ "$got" == "$RC_VERSION" ]]; then
|
||||
ok "V2 with IPK_VERSION set the control Version is IPK_VERSION ($got), file named from PKG_VERSION"
|
||||
else
|
||||
bad "V2 with IPK_VERSION set the control Version is '$got', want $RC_VERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── P1 and P2. Neither package ships the dnsmasq drop-in ────────────────────
|
||||
# OpenWrt's dnsmasq builds its config from UCI and reads no directory under
|
||||
# /etc, so .fips forwarding comes from the UCI entry 90-fips-setup adds. The
|
||||
|
||||
Reference in New Issue
Block a user