name: OpenWrt Package on: push: 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: 32-bit MIPS lacks AtomicU64; needs portable-atomic crate # - 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: 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://relay.primal.net" 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://relay.primal.net" 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