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:
|
outputs:
|
||||||
package_version: ${{ steps.version.outputs.package_version }}
|
package_version: ${{ steps.version.outputs.package_version }}
|
||||||
apk_version: ${{ steps.version.outputs.apk_version }}
|
apk_version: ${{ steps.version.outputs.apk_version }}
|
||||||
|
ipk_version: ${{ steps.version.outputs.ipk_version }}
|
||||||
release_channel: ${{ steps.channel.outputs.release_channel }}
|
release_channel: ${{ steps.channel.outputs.release_channel }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
||||||
@@ -41,15 +42,28 @@ jobs:
|
|||||||
# in the .apk metadata. apk_version is built directly from the same
|
# in the .apk metadata. apk_version is built directly from the same
|
||||||
# structured inputs (tag, or commit height) — no reparse of the
|
# structured inputs (tag, or commit height) — no reparse of the
|
||||||
# flattened package_version. See packaging/openwrt-apk/apk-version.sh.
|
# 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
|
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
|
||||||
echo "package_version=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
|
echo "package_version=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||||
echo "apk_version=$(sh packaging/openwrt-apk/apk-version.sh tag "${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
|
else
|
||||||
BRANCH=$(echo "$GITHUB_REF_NAME" | sed 's|/|-|g')
|
BRANCH=$(echo "$GITHUB_REF_NAME" | sed 's|/|-|g')
|
||||||
HEIGHT=$(git rev-list --count HEAD)
|
HEIGHT=$(git rev-list --count HEAD)
|
||||||
HASH=$(git rev-parse --short HEAD)
|
HASH=$(git rev-parse --short HEAD)
|
||||||
echo "package_version=${BRANCH}.${HEIGHT}.${HASH}" >> "$GITHUB_OUTPUT"
|
echo "package_version=${BRANCH}.${HEIGHT}.${HASH}" >> "$GITHUB_OUTPUT"
|
||||||
echo "apk_version=$(sh packaging/openwrt-apk/apk-version.sh dev "${HEIGHT}")" >> "$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
|
fi
|
||||||
|
|
||||||
- name: Determine release channel
|
- name: Determine release channel
|
||||||
@@ -332,6 +346,7 @@ jobs:
|
|||||||
- name: Build .ipk
|
- name: Build .ipk
|
||||||
env:
|
env:
|
||||||
PKG_VERSION: ${{ needs.determine-versioning.outputs.package_version }}
|
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"
|
run: ./packaging/openwrt-ipk/build-ipk.sh --arch ${{ matrix.build_arch }} --bin-dir "$GITHUB_WORKSPACE/bins"
|
||||||
|
|
||||||
- name: Install shellcheck (if missing)
|
- name: Install shellcheck (if missing)
|
||||||
|
|||||||
@@ -14,7 +14,13 @@
|
|||||||
# arm 32-bit ARM routers (Cortex-A7)
|
# arm 32-bit ARM routers (Cortex-A7)
|
||||||
# x86_64 x86 routers / VMs
|
# 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:
|
# Prerequisites:
|
||||||
# cargo install cargo-zigbuild
|
# cargo install cargo-zigbuild
|
||||||
@@ -86,8 +92,13 @@ DIST_DIR="$PROJECT_ROOT/dist"
|
|||||||
|
|
||||||
PKG_NAME="fips"
|
PKG_NAME="fips"
|
||||||
PKG_VERSION="${PKG_VERSION:-$(cd "$PROJECT_ROOT" && git describe --tags --always --dirty 2>/dev/null || echo "0.1.0")}"
|
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
|
# 1. Obtain binaries
|
||||||
@@ -192,7 +203,7 @@ PKG_SIZE=$(du -sk "$DATA_DIR" | cut -f1)
|
|||||||
|
|
||||||
cat > "$CONTROL_DIR/control" <<EOF
|
cat > "$CONTROL_DIR/control" <<EOF
|
||||||
Package: $PKG_NAME
|
Package: $PKG_NAME
|
||||||
Version: $PKG_VERSION
|
Version: $IPK_VERSION
|
||||||
Architecture: $OPENWRT_ARCH
|
Architecture: $OPENWRT_ARCH
|
||||||
Maintainer: FIPS Network
|
Maintainer: FIPS Network
|
||||||
Section: net
|
Section: net
|
||||||
|
|||||||
@@ -23,6 +23,26 @@
|
|||||||
# would leave checksums-linux.txt naming a file the release does not have).
|
# would leave checksums-linux.txt naming a file the release does not have).
|
||||||
# Those three are read from the text, not executed.
|
# 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
|
# 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
|
# 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,
|
# 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 '~' '-'" \
|
expect_text "$WORK/deb.build.sh" "tr '~' '-'" \
|
||||||
"the .deb build renames a '~' in the package file name"
|
"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
|
echo
|
||||||
if [ "$FAILED" -eq 0 ]; then
|
if [ "$FAILED" -eq 0 ]; then
|
||||||
echo "check-package-versions: all checks passed"
|
echo "check-package-versions: all checks passed"
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ fi
|
|||||||
PKG_VERSION="pkgtest.$$"
|
PKG_VERSION="pkgtest.$$"
|
||||||
TMP="$(mktemp -d)" || { echo "package-test: mktemp failed" >&2; exit 2; }
|
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() {
|
harness_fail() {
|
||||||
echo "package-test: $*" >&2
|
echo "package-test: $*" >&2
|
||||||
@@ -227,7 +227,9 @@ done
|
|||||||
# ── Build the .ipk ──────────────────────────────────────────────────────────
|
# ── Build the .ipk ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
echo "==> build-ipk.sh"
|
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" \
|
bash "$PROJECT_ROOT/packaging/openwrt-ipk/build-ipk.sh" --arch x86_64 --bin-dir "$BINS" \
|
||||||
> "$TMP/build-ipk.log" 2>&1; then
|
> "$TMP/build-ipk.log" 2>&1; then
|
||||||
cat "$TMP/build-ipk.log" >&2
|
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" \
|
tar -xzf "$IPK" -O ./control.tar.gz | tar -tzf - > "$TMP/ipk-control" \
|
||||||
|| harness_fail "cannot list control.tar.gz in $IPK"
|
|| 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 ────────────────────
|
# ── P1 and P2. Neither package ships the dnsmasq drop-in ────────────────────
|
||||||
# OpenWrt's dnsmasq builds its config from UCI and reads no directory under
|
# 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
|
# /etc, so .fips forwarding comes from the UCI entry 90-fips-setup adds. The
|
||||||
|
|||||||
Reference in New Issue
Block a user