#===================================================================================== # https://github.com/ophub/fnnas # Description: Build FnNAS Image #===================================================================================== name: Build FnNAS Image on: repository_dispatch: workflow_dispatch: inputs: fnnas_base_version: description: "Select fnnas base image version" required: false default: "rockchip_1253" type: choice options: - amlogic_1252 - amlogic_693 - rockchip_1253 - rockchip_692 - allwinner_1254 - allwinner_691 fnnas_board: description: "Select fnnas device board" required: false default: "rockchip50" type: choice options: - allwinner - amlogic - rockchip50 - rockchip50_100 - rockchip100_150 - a311d - a311d-oes - alark35-3500 - anas3035 - aio-3399b - aio-3399c - aio-3399c-ai - bdkj-bd-one - bdy-g18-pro - bdy-g98 - beikeyun - boca-tcn100 - boca-tcn200 - bt-100m - bt-1000m - cd1000 - chainedbox - cm3588-nas - crrc - dc-a588 - dg3399 - dg-nas-lite - dg-tn3568 - dlfr100 - e20c - e25 - eaidk-610 - easepi-a2 - easepi-r2 - emb3531 - fine3399 - firefly-itx-3588j - firefly-jd4 - firefly-rk3399 - fmx1-pro - fmx1-pro-b - gea-6319 - gzpeite-p01 - h28k - h618-devboard - h66k - h68k - h69k - h88k - h88k-v3 - h96-max-m2 - hk1-rbox-k8s - hs530r - ht2 - hugsun-x99 - indiedroid-nova - ipc-r - jp-tvbox - king3399 - kylin3399 - lckfb-tspi - lckfb-tspi-3m - leez - lubancat-1 - lubancat-2 - lubancat-3 - lubancat-4 - lx-r3s - lyt-t68m - lz-d3568 - lz-d3588 - lz-k3568 - md1000 - mrkaio-m68s - nanopc-t4 - nanopc-t6 - nanopi-m5 - nanopi-r5c - nanopi-r5s - nanopi-som - nsy-g16-plus - nsy-g68-plus - orangepi-3b - orangepi-5-plus - orangepi-5-pro - orangepi-5-ultra - orangepi-5b - orangepi-zero3 - painet-p3 - panther-x2 - photonicat - r66s - r68s - recomputer-rk3576-devkit - renegade-rk3328 - rk3318-box - roceos-k40pro - rock-5-itx - rock-3b - rock-3c - rock5b - rock5c - ruisen-box - s905 - s905-beelink-mini - s905-mxqpro-plus - s905d - s905d-ki-pro - s905d-sml5442tw - s905d3-khadas-vim3l - s905l - s905l-aurora-1s - s905l-b860av21u - s905l-mg101 - s905l2 - s905l2-e900v21e - s905l2-wojia - s905l3 - s905l3-cm211 - s905l3-unt400g1 - s905l3-unt402a - s905l3a - s905l3a-cm311 - s905l3a-m401a - s905l3b - s905l3b-e900v21d - s905l3b-e900v22d - s905l3b-e900v22e - s905l3b-ip103h - s905l3b-rg020et-ca - s905l3b-unt403a - s905lb-ipbs9505 - s905lb-q96-mini - s905lb-r3300l - s905mb - s905w - s905w-w95 - s905w-x96-mini - s905w-x96w - s905x - s905x-b860h - s905x-nexbox-a95x - s905x-t95 - s905x-tbee - s905x-tx9 - s905x2 - s905x2-km3 - s905x2-x96max-2g - s905x2-hg680fj - s905x3 - s905x3-2101 - s905x3-a100 - s905x3-a95xf3 - s905x3-a95xf3-gb - s905x3-b - s905x3-h96max - s905x3-hk1 - s905x3-ip1001m - s905x3-q1 - s905x3-q2 - s905x3-tox1 - s905x3-tx3 - s905x3-tx3-bz - s905x3-ugoosx3 - s905x3-whale - s905x3-x88-pro-x3 - s905x3-x96air - s905x3-x96air-gb - s905x3-x96max - s912 - s912-h96pro-plus - s912-m8s-pro - s912-nexbox-a1 - s912-nexbox-a2 - s912-onecloudpro - s912-phicomm-t1 - s912-t95z-plus - s912-tx8-max - s912-tx9-pro-2g - s912-tx9-pro-3g - s912-x92 - s912-zyxq-fake - s922x - s922x-ct2000 - s922x-gtking - s922x-gtkingpro-h - s922x-odroid-n2 - s922x-oes-plus - s922x-reva - s922x-ugoos-am6 - seewo-sv21 - seewo-sv50 - smart-am40 - smart-am60 - station-m1 - station-m2 - station-p2 - sv-33a6x - sv-33a6x-vpu - swan1-w28 - sw799 - taram - tanix-tx6 - tb-ls3399 - tn3399 - tpm312 - tqc-a01 - tvi3315a - vontar-h618 - vplus - wocyber-a3 - wxy-oect - wxy-oect-mod - x88pro13 - x88pro20 - x98h - xiaobao - yskj - zcube1-max - zk-r39a - zysj kernel_repo: description: "Set fnnas kernel repository" required: false default: "ophub/fnnas" type: choice options: - ophub/fnnas fnnas_kernel: description: "Select fnnas kernel version" required: false default: "6.18.y" type: choice options: - 6.18.y - 6.12.y auto_kernel: description: "Auto use the latest kernel" required: false default: true type: boolean rootfs_expand: description: "Set rootfs expansion size(GiB)" required: false default: "16" fnnas_url: description: "Custom fnnas image download url" required: false default: "" builder_name: description: "Set fnnas builder signature" required: false default: "ophub" type: choice options: - ophub - angel - yourname env: TZ: Etc/UTC jobs: build: runs-on: ubuntu-26.04-arm if: ${{ github.event.repository.owner.id == github.event.sender.id }} permissions: contents: write steps: - name: Checkout uses: actions/checkout@v6 - name: Initialize the build environment id: init env: DEBIAN_FRONTEND: noninteractive run: | docker rmi -f $(docker images -q) 2>/dev/null || true [[ -n "${AGENT_TOOLSDIRECTORY}" ]] && sudo rm -rf "${AGENT_TOOLSDIRECTORY}" sudo rm -rf /usr/share/dotnet /usr/local/lib/android 2>/dev/null sudo swapoff -a sudo rm -f /swapfile /mnt/swapfile sudo apt-get -y update sudo apt-get -y purge azure-cli ghc* zulu* llvm* firefox google* dotnet* powershell openjdk* mongodb* moby* || true sudo apt-get -y install $(curl -fsSL https://ophub.org/ubuntu2604-make-fnnas-depends) sudo systemctl daemon-reload #sudo apt-get -y full-upgrade sudo apt-get -y autoremove --purge sudo apt-get clean sudo sed -i '/NVM_DIR/d;/skel/d' /root/{.bashrc,.profile} sudo rm -rf ~/{.cargo,.dotnet,.rustup} sudo timedatectl set-timezone "${TZ:-Etc/UTC}" sudo ntpdate ntp.ubuntu.com 0.pool.ntp.org || true sudo timedatectl set-ntp true date -u timedatectl status || true echo "status=success" >> ${GITHUB_OUTPUT} - name: Create virtual disk for extended storage id: disk run: | # Get available space in exact GB root_avail=$(df --block-size=1G / | tail -1 | awk '{print $4}') root_size=$(( root_avail - 4 )) [[ "${root_size}" -gt 0 ]] || { echo "Error: Insufficient space on /: ${root_avail}G available"; exit 1; } sudo truncate -s "${root_size}"G /root.img build_dev=$(sudo losetup --find --show /root.img) echo "Using /: ${root_size}G" sudo mkfs.xfs -f -i sparse=0 -b size=4096 "${build_dev}" sudo mkdir -p /builder sudo mount "${build_dev}" /builder sudo chown -R runner:runner /builder df -Th echo "status=success" >> ${GITHUB_OUTPUT} - name: Download fnnas image [ ${{ inputs.fnnas_base_version }} ] id: down working-directory: /builder if: (!cancelled()) env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | # Create directory [[ -d "fnnas" ]] || mkdir -p fnnas input_url="${{ inputs.fnnas_url }}" fnnas_base_version="${{ inputs.fnnas_base_version }}" [[ -n "${fnnas_base_version}" ]] && build_tag="fnnas_${fnnas_base_version}" || build_tag="fnnas_save" # ------------------------------------------------------- # Case A: URL specified # ------------------------------------------------------- if [[ -n "${input_url}" ]]; then echo "Custom URL detected: ${input_url}" # Remove any parameters that may be included in the URL (?...) clean_url="${input_url%%\?*}" # Regex match GitHub Release URL format # Format: https://github.com///releases/download// regex="github\.com/([^/]+)/([^/]+)/releases/download/([^/]+)/(.+)" if [[ "${clean_url}" =~ $regex ]]; then # Extract information owner="${BASH_REMATCH[1]}" repo="${BASH_REMATCH[2]}" tag="${BASH_REMATCH[3]}" filename="${BASH_REMATCH[4]}" echo "Detected GitHub Release URL." echo "Repo: $owner/$repo | Tag: $tag | File: $filename" # Use gh cli to download gh release download "${tag}" \ --repo "${owner}/${repo}" \ --pattern "${filename}" \ --dir "fnnas" \ --clobber # Check download result if [[ -f "fnnas/${filename}" ]]; then echo "Download success via gh-cli." echo "status=success" >> ${GITHUB_OUTPUT} exit 0 else echo "Error: gh download failed." exit 1 fi else # If not a standard Release URL (e.g., API URL or third-party URL), fallback to curl echo "Using curl fallback..." latest_url="${input_url}" fnnas_filename=$(basename "${clean_url}") fi # ------------------------------------------------------- # Case B: Automatically fetch the latest version # ------------------------------------------------------- else echo "Auto-detecting latest release..." latest_version=$(curl -s \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ https://api.github.com/repos/ophub/fnnas/releases?per_page=20 | \ jq -r --arg RTK "fnnas_base_image" \ --arg BOARD "${fnnas_base_version}" \ '[.[] | select(.tag_name | contains($RTK))] | map(.assets[] | select(.name | contains($BOARD) and endswith(".img.xz"))) | sort_by(.updated_at) | reverse | .[0] | {data: .updated_at, url: .url, name: .name}') [[ -z "${latest_version}" || "${latest_version}" == "null" ]] && { echo "Error: Failed to resolve FnNAS image download URL." } latest_url="$(echo ${latest_version} | jq -r '.url')" fnnas_filename="$(echo ${latest_version} | jq -r '.name')" fi # ------------------------------------------------------- # Execute curl download (only if the above gh did not run) # ------------------------------------------------------- echo "Downloading: ${fnnas_filename}" echo "Source: ${latest_url}" curl -fsSL \ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ -H "Accept: application/octet-stream" \ "${latest_url}" \ -o "fnnas/${fnnas_filename}" [[ "${?}" -ne "0" ]] && echo "Error: Download failed." && exit 1 # Create directory symlinks for packaging ln -sf /builder/fnnas ${{ github.workspace }}/fnnas ln -sf /builder/fnnas /home/runner/work/_actions/ophub/fnnas/main/fnnas echo "build_tag=${build_tag}" >> ${GITHUB_ENV} echo "status=success" >> ${GITHUB_OUTPUT} - name: Package FnNAS for target boards if: ${{ steps.down.outputs.status == 'success' && !cancelled() }} uses: ophub/fnnas@main with: fnnas_path: fnnas/*.img.xz fnnas_board: ${{ inputs.fnnas_board }} fnnas_kernel: ${{ inputs.fnnas_kernel }} auto_kernel: ${{ inputs.auto_kernel }} kernel_repo: ${{ inputs.kernel_repo }} rootfs_expand: ${{ inputs.rootfs_expand }} builder_name: ${{ inputs.builder_name }} - name: Upload packaged FnNAS to Release uses: ophub/upload-to-releases@main if: ${{ env.PACKAGED_STATUS == 'success' && !cancelled() }} with: tag: ${{ env.build_tag }} artifacts: ${{ env.PACKAGED_OUTPUTPATH }}/* allow_updates: true remove_artifacts: false replaces_artifacts: true make_latest: true gh_token: ${{ secrets.GITHUB_TOKEN }} body: | - Built based on the fnnas.com [official Arm64 image](https://github.com/ophub/fnnas/releases/tag/fnnas_base_image), fully preserving the original official system experience. Extends support to a wider range of Amlogic, Rockchip, and Allwinner devices. - 基于 fnnas.com [官方 Arm64 镜像](https://github.com/ophub/fnnas/releases/tag/fnnas_base_image) 构建, 完整保留官方原版系统体验。 扩展支持更多基于 Amlogic、Rockchip 及 Allwinner 平台的设备。