mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
Investigated the mipsel-unknown-linux-musl build of this branch on a
Linux/x86_64 host. ring 0.17, portable-atomic, and the fips codebase
itself all compile cleanly for that target — the AtomicU64 portability
work is already done. The actual blocker is in the nostr-relay-pool 0.44
transitive dep, which uses std::sync::atomic::AtomicU64 directly in
src/relay/{stats,ping,flags}.rs. Verified fixable with a 6-line
portable-atomic patch via a [patch.crates-io] shim during local testing.
Updating the comment so the next person looking at this matrix has the
right starting point.
No functional change.
530 lines
18 KiB
YAML
530 lines
18 KiB
YAML
name: OpenWrt Package
|
|
on:
|
|
push:
|
|
branches:
|
|
- master
|
|
- maint
|
|
- next
|
|
tags:
|
|
- "v*"
|
|
pull_request:
|
|
workflow_dispatch:
|
|
inputs:
|
|
arch:
|
|
description: "Target architecture (leave empty to build all)"
|
|
required: false
|
|
default: ""
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
PACKAGE_NAME: "fips"
|
|
|
|
jobs:
|
|
determine-versioning:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
package_version: ${{ steps.version.outputs.package_version }}
|
|
release_channel: ${{ steps.channel.outputs.release_channel }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Derive package version
|
|
id: version
|
|
shell: bash
|
|
run: |
|
|
: ${GITHUB_OUTPUT:=/tmp/github_output}
|
|
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
|
|
echo "package_version=${GITHUB_REF_NAME}" >> "$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"
|
|
fi
|
|
|
|
- name: Determine release channel
|
|
id: channel
|
|
shell: bash
|
|
run: |
|
|
: ${GITHUB_OUTPUT:=/tmp/github_output}
|
|
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
|
|
TAG_NAME=${GITHUB_REF#refs/tags/}
|
|
if [[ $TAG_NAME =~ ^v[0-9]+\.[0-9]+\.[0-9]+-alpha ]]; then
|
|
echo "release_channel=alpha" >> "$GITHUB_OUTPUT"
|
|
elif [[ $TAG_NAME =~ ^v[0-9]+\.[0-9]+\.[0-9]+-beta ]]; then
|
|
echo "release_channel=beta" >> "$GITHUB_OUTPUT"
|
|
elif [[ $TAG_NAME =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
echo "release_channel=stable" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "release_channel=dev" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
else
|
|
echo "release_channel=dev" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
build:
|
|
name: Build .ipk (${{ matrix.openwrt_arch }})
|
|
runs-on: ubuntu-latest
|
|
needs: determine-versioning
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- build_arch: aarch64
|
|
openwrt_arch: aarch64_cortex-a53
|
|
rust_target: aarch64-unknown-linux-musl
|
|
rust_channel: stable
|
|
# MT3000, MT6000, Flint 2, RPi 3/4/5
|
|
# MIPS disabled: nostr-relay-pool 0.44 uses std::sync::atomic::AtomicU64
|
|
# directly (fips's own atomics already use portable_atomic). Re-enable
|
|
# once an upstream portable-atomic patch lands (or via [patch.crates-io]).
|
|
# - build_arch: mipsel
|
|
# openwrt_arch: mipsel_24kc
|
|
# rust_target: mipsel-unknown-linux-musl
|
|
# rust_channel: nightly
|
|
# - build_arch: mips
|
|
# openwrt_arch: mips_24kc
|
|
# rust_target: mips-unknown-linux-musl
|
|
# rust_channel: nightly
|
|
- build_arch: x86_64
|
|
openwrt_arch: x86_64
|
|
rust_target: x86_64-unknown-linux-musl
|
|
rust_channel: stable
|
|
# x86 routers / VMs
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Set SOURCE_DATE_EPOCH from git
|
|
run: echo "SOURCE_DATE_EPOCH=$(git log -1 --format=%ct)" >> "$GITHUB_ENV"
|
|
|
|
- name: Initialize
|
|
run: |
|
|
PACKAGE_FILENAME=${{ env.PACKAGE_NAME }}_${{ needs.determine-versioning.outputs.package_version }}_${{ matrix.openwrt_arch }}.ipk
|
|
echo "PACKAGE_FILENAME=$PACKAGE_FILENAME" >> $GITHUB_ENV
|
|
|
|
- name: Install Rust toolchain (stable)
|
|
if: matrix.rust_channel == 'stable'
|
|
uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: ${{ matrix.rust_target }}
|
|
|
|
- name: Install Rust toolchain (nightly, Tier 3)
|
|
if: matrix.rust_channel == 'nightly'
|
|
uses: dtolnay/rust-toolchain@nightly
|
|
with:
|
|
components: rust-src
|
|
|
|
- name: Cache Cargo registry + build
|
|
if: ${{ env.ACT != 'true' }}
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
target/${{ matrix.rust_target }}
|
|
key: openwrt-${{ matrix.rust_target }}-${{ hashFiles('**/Cargo.lock') }}
|
|
restore-keys: |
|
|
openwrt-${{ matrix.rust_target }}-
|
|
|
|
- name: Install cargo-zigbuild
|
|
run: cargo install cargo-zigbuild --version 0.19.8 --locked
|
|
|
|
- name: Install zig (required by cargo-zigbuild)
|
|
run: |
|
|
ZIG_VERSION="0.13.0"
|
|
ARCH=$(uname -m)
|
|
case "$ARCH" in
|
|
x86_64|amd64) ZIG_ARCH="x86_64" ;;
|
|
aarch64|arm64) ZIG_ARCH="aarch64" ;;
|
|
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
|
|
esac
|
|
curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-${ZIG_ARCH}-${ZIG_VERSION}.tar.xz" | sudo tar xJ -C /opt
|
|
sudo ln -sf /opt/zig-linux-${ZIG_ARCH}-${ZIG_VERSION}/zig /usr/local/bin/zig
|
|
zig version
|
|
|
|
- name: Install llvm-strip
|
|
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends llvm
|
|
|
|
- name: Install nak
|
|
shell: bash
|
|
run: |
|
|
NAK_VERSION="0.16.2"
|
|
ARCH=$(uname -m)
|
|
case "$ARCH" in
|
|
x86_64|amd64) NAK_ARCH="amd64" ;;
|
|
aarch64|arm64) NAK_ARCH="arm64" ;;
|
|
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
|
|
esac
|
|
curl -fsSL "https://github.com/fiatjaf/nak/releases/download/v${NAK_VERSION}/nak-v${NAK_VERSION}-linux-${NAK_ARCH}" \
|
|
-o /usr/local/bin/nak
|
|
chmod +x /usr/local/bin/nak
|
|
nak --version
|
|
|
|
- name: Install jq
|
|
run: |
|
|
if ! command -v jq &>/dev/null; then
|
|
sudo apt-get update && sudo apt-get install -y jq
|
|
fi
|
|
|
|
# Priority: HIVE_CI_NSEC from env (loom job) > repo secret > generate ephemeral
|
|
- name: Resolve signing key
|
|
id: keys
|
|
shell: bash
|
|
env:
|
|
SECRET_NSEC: ${{ secrets.HIVE_CI_NSEC }}
|
|
run: |
|
|
: ${GITHUB_OUTPUT:=/tmp/github_output}
|
|
if [ -n "${HIVE_CI_NSEC:-}" ]; then
|
|
echo "Using HIVE_CI_NSEC from loom job environment"
|
|
NSEC="$HIVE_CI_NSEC"
|
|
elif [ -n "$SECRET_NSEC" ]; then
|
|
echo "Using HIVE_CI_NSEC from repository secrets"
|
|
NSEC="$SECRET_NSEC"
|
|
else
|
|
echo "No nsec provided -- generating ephemeral keypair"
|
|
NSEC=$(nak key generate)
|
|
fi
|
|
|
|
PUBKEY=$(echo "$NSEC" | nak key public)
|
|
echo "::add-mask::$NSEC"
|
|
echo "nsec=$NSEC" >> "$GITHUB_OUTPUT"
|
|
echo "pubkey=$PUBKEY" >> "$GITHUB_OUTPUT"
|
|
echo "Publisher pubkey (hex): $PUBKEY"
|
|
- name: Build .ipk
|
|
env:
|
|
PKG_VERSION: ${{ needs.determine-versioning.outputs.package_version }}
|
|
LLVM_STRIP: llvm-strip
|
|
run: ./packaging/openwrt-ipk/build-ipk.sh --arch ${{ matrix.build_arch }}
|
|
|
|
- name: Install shellcheck (if missing)
|
|
shell: bash
|
|
run: |
|
|
if ! command -v shellcheck >/dev/null 2>&1; then
|
|
sudo apt-get update && sudo apt-get install -y --no-install-recommends shellcheck
|
|
fi
|
|
shellcheck --version
|
|
|
|
- name: Lint shipped shell scripts
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
FILES_DIR=packaging/openwrt-ipk/files
|
|
# Scripts shipped inside the .ipk. The init scripts use the OpenWrt
|
|
# `#!/bin/sh /etc/rc.common` shebang; tell shellcheck to treat them
|
|
# as POSIX sh and silence the unrecognized-shebang warning (SC1008).
|
|
# SC2317 (unreachable command) fires on rc.common's externally-invoked
|
|
# start_service/stop_service/reload_service hooks.
|
|
TARGETS=(
|
|
"$FILES_DIR/etc/init.d/fips"
|
|
"$FILES_DIR/etc/init.d/fips-gateway"
|
|
"$FILES_DIR/etc/fips/firewall.sh"
|
|
"$FILES_DIR/etc/hotplug.d/net/99-fips"
|
|
"$FILES_DIR/etc/uci-defaults/90-fips-setup"
|
|
)
|
|
fail=0
|
|
for f in "${TARGETS[@]}"; do
|
|
if [ ! -f "$f" ]; then
|
|
echo "FAIL: missing $f"
|
|
fail=1
|
|
continue
|
|
fi
|
|
echo "==> shellcheck $f"
|
|
if shellcheck --shell=sh --exclude=SC1008,SC2317,SC2034,SC3043,SC2086,SC2089,SC2090 "$f"; then
|
|
echo " PASS"
|
|
else
|
|
echo " FAIL"
|
|
fail=1
|
|
fi
|
|
done
|
|
if [ "$fail" -ne 0 ]; then
|
|
echo "shellcheck FAILED"
|
|
exit 1
|
|
fi
|
|
echo "shellcheck PASS (${#TARGETS[@]} scripts)"
|
|
|
|
- name: Sysctl drop-in syntax check
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
FILES_DIR=packaging/openwrt-ipk/files
|
|
TARGETS=(
|
|
"$FILES_DIR/etc/sysctl.d/fips-gateway.conf"
|
|
"$FILES_DIR/etc/sysctl.d/fips-bridge.conf"
|
|
)
|
|
fail=0
|
|
for conf in "${TARGETS[@]}"; do
|
|
if [ ! -f "$conf" ]; then
|
|
echo "FAIL: missing $conf"
|
|
fail=1
|
|
continue
|
|
fi
|
|
echo "==> validating $conf"
|
|
lineno=0
|
|
file_ok=1
|
|
while IFS= read -r line || [ -n "$line" ]; do
|
|
lineno=$((lineno + 1))
|
|
# Skip comments and blank lines.
|
|
case "$line" in
|
|
''|\#*) continue ;;
|
|
esac
|
|
# Match: <key> = <value> where key is dotted lower-id and value is
|
|
# an integer (sysctl drop-ins shipped here are all numeric toggles).
|
|
if ! [[ "$line" =~ ^[a-z0-9_.-]+[[:space:]]*=[[:space:]]*-?[0-9]+[[:space:]]*$ ]]; then
|
|
echo " FAIL line $lineno: $line"
|
|
file_ok=0
|
|
fi
|
|
done < "$conf"
|
|
if [ "$file_ok" -eq 1 ]; then
|
|
echo " PASS"
|
|
else
|
|
fail=1
|
|
fi
|
|
done
|
|
if [ "$fail" -ne 0 ]; then
|
|
echo "sysctl drop-in syntax check FAILED"
|
|
exit 1
|
|
fi
|
|
echo "sysctl drop-in syntax check PASS"
|
|
|
|
- name: Verify ipk structural integrity
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
IPK="dist/${{ env.PACKAGE_FILENAME }}"
|
|
if [ ! -f "$IPK" ]; then
|
|
echo "FAIL: produced ipk not found at $IPK"
|
|
exit 1
|
|
fi
|
|
echo "==> file type:"
|
|
file "$IPK"
|
|
|
|
# OpenWrt .ipk = tar.gz containing debian-binary + control.tar.gz +
|
|
# data.tar.gz (NOT an ar archive like Debian's .deb).
|
|
WORK=$(mktemp -d)
|
|
trap 'rm -rf "$WORK"' EXIT
|
|
|
|
tar -xzf "$IPK" -C "$WORK"
|
|
echo "==> top-level entries:"
|
|
ls -la "$WORK"
|
|
|
|
# Top-level structural assertions.
|
|
fail=0
|
|
for entry in debian-binary control.tar.gz data.tar.gz; do
|
|
if [ ! -f "$WORK/$entry" ]; then
|
|
echo "FAIL: missing top-level $entry"
|
|
fail=1
|
|
else
|
|
echo " PASS top-level: $entry"
|
|
fi
|
|
done
|
|
if [ "$fail" -ne 0 ]; then exit 1; fi
|
|
|
|
# debian-binary content sanity.
|
|
dbin_content=$(cat "$WORK/debian-binary" | tr -d '[:space:]')
|
|
if [ "$dbin_content" != "2.0" ]; then
|
|
echo "FAIL: debian-binary content is '$dbin_content' (expected 2.0)"
|
|
exit 1
|
|
fi
|
|
echo " PASS debian-binary content: 2.0"
|
|
|
|
# Inspect data.tar.gz contents.
|
|
DATA_LIST="$WORK/data.list"
|
|
tar -tzf "$WORK/data.tar.gz" > "$DATA_LIST"
|
|
echo "==> data.tar.gz entry count: $(wc -l < "$DATA_LIST")"
|
|
|
|
# Required filesystem entries inside data.tar.gz. Entries are
|
|
# produced with a leading "./" by build-ipk.sh.
|
|
REQUIRED=(
|
|
./usr/bin/fips
|
|
./usr/bin/fipsctl
|
|
./usr/bin/fipstop
|
|
./usr/bin/fips-gateway
|
|
./etc/init.d/fips
|
|
./etc/init.d/fips-gateway
|
|
./etc/fips/fips.yaml
|
|
./etc/fips/firewall.sh
|
|
./etc/dnsmasq.d/fips.conf
|
|
./etc/sysctl.d/fips-gateway.conf
|
|
./etc/sysctl.d/fips-bridge.conf
|
|
./etc/hotplug.d/net/99-fips
|
|
./etc/uci-defaults/90-fips-setup
|
|
./lib/upgrade/keep.d/fips
|
|
)
|
|
for path in "${REQUIRED[@]}"; do
|
|
if grep -Fxq "$path" "$DATA_LIST"; then
|
|
echo " PASS data: $path"
|
|
else
|
|
echo " FAIL data: missing $path"
|
|
fail=1
|
|
fi
|
|
done
|
|
|
|
# Inspect control.tar.gz: must contain control file + maintainer scripts.
|
|
CTRL_LIST="$WORK/control.list"
|
|
tar -tzf "$WORK/control.tar.gz" > "$CTRL_LIST"
|
|
echo "==> control.tar.gz entry count: $(wc -l < "$CTRL_LIST")"
|
|
for path in ./control ./conffiles ./postinst ./prerm; do
|
|
if grep -Fxq "$path" "$CTRL_LIST"; then
|
|
echo " PASS control: $path"
|
|
else
|
|
echo " FAIL control: missing $path"
|
|
fail=1
|
|
fi
|
|
done
|
|
|
|
if [ "$fail" -ne 0 ]; then
|
|
echo "ipk structural verification FAILED"
|
|
exit 1
|
|
fi
|
|
echo "ipk structural verification PASS"
|
|
|
|
- name: SHA-256 hashes
|
|
run: |
|
|
echo "==> Binaries:"
|
|
sha256sum target/${{ matrix.rust_target }}/release/fips target/${{ matrix.rust_target }}/release/fipsctl target/${{ matrix.rust_target }}/release/fipstop
|
|
echo "==> Package:"
|
|
sha256sum dist/${{ env.PACKAGE_FILENAME }}
|
|
|
|
- name: Upload artifact (GitHub only)
|
|
if: ${{ env.ACT != 'true' }}
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: ${{ env.PACKAGE_FILENAME }}
|
|
path: dist/${{ env.PACKAGE_FILENAME }}
|
|
retention-days: 30
|
|
|
|
- name: Upload to Blossom
|
|
id: blossom_upload
|
|
shell: bash
|
|
env:
|
|
BLOSSOM_SERVER: "https://blossom.primal.net"
|
|
NSEC: ${{ steps.keys.outputs.nsec }}
|
|
run: |
|
|
: ${GITHUB_OUTPUT:=/tmp/github_output}
|
|
|
|
UPLOAD_RESPONSE=$(nak blossom upload \
|
|
--server "$BLOSSOM_SERVER" \
|
|
--sec "$NSEC" \
|
|
"dist/${{ env.PACKAGE_FILENAME }}" < /dev/null)
|
|
|
|
echo "Upload response:"
|
|
echo "$UPLOAD_RESPONSE"
|
|
|
|
FILE_HASH=$(echo "$UPLOAD_RESPONSE" | jq -r '.sha256')
|
|
if [ -z "$FILE_HASH" ] || [ "$FILE_HASH" = "null" ]; then
|
|
echo "Failed to extract hash from upload response"
|
|
exit 1
|
|
fi
|
|
|
|
BLOSSOM_URL="${BLOSSOM_SERVER}/${FILE_HASH}"
|
|
echo "url=$BLOSSOM_URL" >> "$GITHUB_OUTPUT"
|
|
echo "hash=$FILE_HASH" >> "$GITHUB_OUTPUT"
|
|
echo "Uploaded to Blossom: $BLOSSOM_URL"
|
|
|
|
- name: Publish NIP-94 release event
|
|
id: publish
|
|
shell: bash
|
|
env:
|
|
RELAYS: "wss://relay.damus.io wss://nos.lol wss://nostr.mom wss://offchain.pub"
|
|
NSEC: ${{ steps.keys.outputs.nsec }}
|
|
run: |
|
|
: ${GITHUB_OUTPUT:=/tmp/github_output}
|
|
set -e
|
|
|
|
VERSION="${{ needs.determine-versioning.outputs.package_version }}"
|
|
CHANNEL="${{ needs.determine-versioning.outputs.release_channel }}"
|
|
|
|
nak event --sec "$NSEC" -k 1063 \
|
|
-c "FIPS Package: ${{ env.PACKAGE_NAME }} for ${{ matrix.openwrt_arch }}" \
|
|
--tag url="${{ steps.blossom_upload.outputs.url }}" \
|
|
--tag m="application/octet-stream" \
|
|
--tag x="${{ steps.blossom_upload.outputs.hash }}" \
|
|
--tag ox="${{ steps.blossom_upload.outputs.hash }}" \
|
|
--tag filename="${{ env.PACKAGE_FILENAME }}" \
|
|
--tag A="${{ matrix.openwrt_arch }}" \
|
|
--tag v="$VERSION" \
|
|
--tag n="${{ env.PACKAGE_NAME }}" \
|
|
--tag compression="none" \
|
|
> event.json 2> event.err
|
|
|
|
if [ ! -s event.json ]; then
|
|
echo "Failed to create event"
|
|
cat event.err 2>/dev/null || true
|
|
exit 1
|
|
fi
|
|
|
|
echo "=== Event JSON ==="
|
|
cat event.json
|
|
echo "=================="
|
|
|
|
EVENT_ID=$(jq -r '.id' event.json)
|
|
if [ -z "$EVENT_ID" ] || [ "$EVENT_ID" = "null" ]; then
|
|
echo "Failed to extract event ID"
|
|
exit 1
|
|
fi
|
|
|
|
# Publish to relays
|
|
cat event.json | nak event $RELAYS 2>&1
|
|
|
|
echo "eventId=$EVENT_ID" >> "$GITHUB_OUTPUT"
|
|
echo "Published NIP-94 event: $EVENT_ID"
|
|
|
|
- name: Verify NIP-94 event on relays
|
|
if: ${{ steps.publish.outputs.eventId != '' }}
|
|
env:
|
|
EVENT_ID: ${{ steps.publish.outputs.eventId }}
|
|
RELAYS: "wss://relay.damus.io wss://nos.lol wss://nostr.mom wss://offchain.pub"
|
|
run: |
|
|
echo "Verifying event $EVENT_ID on relays..."
|
|
FOUND=0
|
|
for relay in $RELAYS; do
|
|
echo "Checking $relay..."
|
|
RESULT=$(nak req -i "$EVENT_ID" "$relay" 2>/dev/null || echo "")
|
|
if [ -n "$RESULT" ]; then
|
|
echo "Found on $relay"
|
|
FOUND=1
|
|
else
|
|
echo "Not found on $relay"
|
|
fi
|
|
done
|
|
|
|
if [ $FOUND -eq 0 ]; then
|
|
echo "Warning: Event not found on any relay yet (may still be propagating)"
|
|
else
|
|
echo "Event verified on at least one relay"
|
|
fi
|
|
|
|
- name: Build Summary
|
|
run: |
|
|
echo "Build Summary for ${{ matrix.openwrt_arch }}:"
|
|
echo " Package: ${{ env.PACKAGE_FILENAME }}"
|
|
echo " Release EventId: ${{ steps.publish.outputs.eventId }}"
|
|
echo " Blossom URL: ${{ steps.blossom_upload.outputs.url }}"
|
|
|
|
release:
|
|
name: Publish GitHub Release (github only)
|
|
runs-on: ubuntu-latest
|
|
needs: build
|
|
if: startsWith(github.ref, 'refs/tags/')
|
|
permissions:
|
|
contents: write
|
|
|
|
steps:
|
|
- name: Download all .ipk artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
path: dist
|
|
merge-multiple: true
|
|
|
|
- name: Create release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
files: dist/*.ipk
|
|
generate_release_notes: true
|