diff --git a/.github/workflows/validation.yaml b/.github/workflows/validation.yaml index bea26bed4..9705431b4 100644 --- a/.github/workflows/validation.yaml +++ b/.github/workflows/validation.yaml @@ -282,6 +282,40 @@ jobs: if: steps.test-nodebuilder.outcome == 'failure' run: printf '%s\n' "Review the step 'Test the console output' above." && exit 1 + run-nodebuilder-freebsd: + name: Test source on FreeBSD + needs: [changes, shell-lint, yaml-lint] + if: >- + ${{ + github.event.action == 'prerelease' || + github.event.action == 'published' || + needs.changes.outputs.ci == 'true' || + needs.changes.outputs.dependencies == 'true' || + needs.changes.outputs.shell == 'true' + }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Test the console output + uses: vmactions/freebsd-vm@v1 + timeout-minutes: 180 + with: + prepare: | + uname -a + cat /etc/os-release + nproc + sysctl -a | grep ' memory' | head -2 + df -h + date -u + run: | + /bin/sh -x ./test/test_nodebuilder --ref "${GITHUB_SHA}" + [ -f "${HOME}/.bitcoin/debug.log" ] && cp "${HOME}/.bitcoin/debug.log" . + - name: Save Bitcoin Core log as artifact + uses: actions/upload-artifact@v4 + with: + name: freebsd-source-bitcoin-debug.log + path: /home/runner/work/nodebuilder/nodebuilder/debug.log + run-nodebuilder-docker: name: Docker image for ${{ matrix.container }} needs: [changes, dockerfile-lint, shell-lint, yaml-lint] diff --git a/nodebuilder b/nodebuilder index 248571714..db8ecb64f 100755 --- a/nodebuilder +++ b/nodebuilder @@ -66,19 +66,37 @@ compile_bitcoin_from_source() { ./autogen.sh > /dev/null 2> "${STDERR_COMPILE_LOG_FILE}" grep -v 'build-aux' "${STDERR_COMPILE_LOG_FILE}" >&2 || true log_info 'Configuring the build environment.' - if [ "${TARGET_KERNEL}" = 'Darwin' ]; then + case "${TARGET_KERNEL}" in + 'Darwin') ./configure CC=clang CXX=clang++ --without-bdb --enable-suppress-external-warnings > /dev/null - else + ;; + 'FreeBSD') + ./configure --without-bdb --enable-suppress-external-warnings MAKE=gmake > "${HOME}/configure_results" + ;; + *) ./configure --without-bdb --enable-suppress-external-warnings > /dev/null - fi + ;; + esac log_info 'Compiling source code. Please wait.' - make --jobs "${SYS_CORES_PLUS_ONE}" > /dev/null 2>&1 + if [ "${TARGET_KERNEL}" = 'FreeBSD' ]; then + gmake --jobs "${SYS_CORES_PLUS_ONE}" > /dev/null 2>&1 + else + make --jobs "${SYS_CORES_PLUS_ONE}" > /dev/null 2>&1 + fi log_info 'Running compile checks. Please wait.' - make --jobs "${SYS_CORES_PLUS_ONE}" check > /dev/null 2> "${STDERR_COMPILE_LOG_FILE}" + if [ "${TARGET_KERNEL}" = 'FreeBSD' ]; then + gmake --jobs "${SYS_CORES_PLUS_ONE}" check > /dev/null 2> "${STDERR_COMPILE_LOG_FILE}" + else + make --jobs "${SYS_CORES_PLUS_ONE}" check > /dev/null 2> "${STDERR_COMPILE_LOG_FILE}" + fi # exclude the two lines before and after 'Ran 3 tests in ' in make check's stdout sed -n '1N;2N;/Ran 3 tests in /{N;N;d;};P;N;D' "${STDERR_COMPILE_LOG_FILE}" >&2 log_info 'Installing Bitcoin Core.' - make install > /dev/null 2>&1 || sudo make install > /dev/null + if [ "${TARGET_KERNEL}" = 'FreeBSD' ]; then + gmake install > /dev/null 2>&1 || sudo gmake install > /dev/null + else + make install > /dev/null 2>&1 || sudo make install > /dev/null + fi cd - > /dev/null rm "${STDERR_COMPILE_LOG_FILE}" rm -rf "${COMPILE_DIRECTORY:?}"/ @@ -193,6 +211,9 @@ ensure_curl_dependency() { Darwin) throw_error "Please install the 'curl' dependency." ;; + FreeBSD) + sudo pkg install -y curl > /dev/null + ;; *) if command -v apk > /dev/null; then apk --quiet add curl @@ -239,6 +260,9 @@ ensure_sudo_dependency() { clear-linux-os) swupd bundle-add sudo > /dev/null ;; + FreeBSD) + pkg install -y sudo > /dev/null + ;; *) if command -v apk > /dev/null; then apk --quiet add sudo @@ -285,6 +309,9 @@ ensure_xargs_dependency() { clear-linux-os) swupd bundle-add findutils > /dev/null ;; + FreeBSD) + sudo pkg install -y findutils > /dev/null + ;; *) if command -v apk > /dev/null; then apk --quiet add findutils @@ -309,11 +336,14 @@ ensure_xargs_dependency() { } get_free_space_in_mib() { - if [ "${TARGET_KERNEL}" = 'Darwin' ]; then + case "${TARGET_KERNEL}" in + 'Darwin' | 'FreeBSD') /bin/df -m "${HOME}" | awk '{print $4}' | sed 1d - else + ;; + *) df --output=avail --block-size='1MiB' "${HOME}" | sed 1d - fi + ;; + esac } get_log_timestamp() { @@ -328,8 +358,8 @@ get_memory_metric_in_mib() { } get_operating_system() { - if [ "${TARGET_KERNEL}" = 'Darwin' ]; then - printf '%s\n' 'Darwin' + if [ "${TARGET_KERNEL}" = 'Darwin' ] || [ "${TARGET_KERNEL}" = 'FreeBSD' ]; then + printf '%s\n' "${TARGET_KERNEL}" else OS_RELEASE_ID="$(grep '^ID=' /etc/os-release | cut -d= -f2)" && readonly OS_RELEASE_ID OS_RELEASE_ID_LIKE="$(grep '^ID_LIKE=' /etc/os-release | cut -d= -f2)" && readonly OS_RELEASE_ID_LIKE @@ -379,6 +409,9 @@ install_build_dependencies() { Darwin) install_build_dependencies_darwin ;; + FreeBSD) + install_build_dependencies_freebsd + ;; *) install_build_command_function='' for package_manager in \ @@ -481,6 +514,15 @@ install_build_dependencies_emerge() { fi } +install_build_dependencies_freebsd() { + readonly BUILD_DEPENDENCIES_URL="${FREEBSD_DEPENDENCIES_BASE_URL}/build_dependencies_freebsd.txt" + command -v torsocks > /dev/null 2>&1 && + dependencies=$(torsocks curl --fail --silent --show-error --location --retry 2 "${BUILD_DEPENDENCIES_URL}") || + dependencies=$(curl --fail --silent --show-error --location --retry 5 "${BUILD_DEPENDENCIES_URL}") + [ -z "${dependencies:-}" ] && throw_error "The list of dependencies is empty." + printf '%s\n' "${dependencies}" | xargs sudo pkg install --yes > /dev/null +} + install_build_dependencies_pacman() { readonly BUILD_DEPENDENCIES_URL="${DEPENDENCIES_BASE_URL}/build_dependencies_pacman.txt" command -v torsocks > /dev/null 2>&1 && @@ -537,6 +579,9 @@ install_runtime_dependencies() { Darwin) install_runtime_dependencies_darwin ;; + FreeBSD) + install_runtime_dependencies_freebsd + ;; *) install_runtime_command_function='' for package_manager in \ @@ -633,6 +678,15 @@ install_runtime_dependencies_emerge() { printf '%s\n' "${dependencies}" | xargs emerge --autounmask-write --jobs "${SYS_CORES_COUNT}" --load-average "${SYS_CORES_PLUS_ONE}" --quiet --quiet-build --quiet-fail } +install_runtime_dependencies_freebsd() { + readonly RUNTIME_DEPENDENCIES_URL="${FREEBSD_DEPENDENCIES_BASE_URL}/runtime_dependencies_freebsd.txt" + command -v torsocks > /dev/null 2>&1 && + dependencies=$(torsocks curl --fail --silent --show-error --location --retry 2 "${RUNTIME_DEPENDENCIES_URL}") || + dependencies=$(curl --fail --silent --show-error --location --retry 5 "${RUNTIME_DEPENDENCIES_URL}") + [ -z "${dependencies}" ] && throw_error 'The list of dependencies is empty.' + printf '%s\n' "${dependencies}" | xargs sudo pkg install --yes > /dev/null +} + install_runtime_dependencies_pacman() { readonly RUNTIME_DEPENDENCIES_URL="${DEPENDENCIES_BASE_URL}/runtime_dependencies_pacman.txt" readonly STDERR_DEPENDENCIES_LOG_FILE="${TEMP_DIRECTORY}/stderr_runtime_dependencies_log" @@ -687,6 +741,9 @@ install_system_updates() { clear-linux-os) install_system_updates_swupd ;; + FreeBSD) + install_system_updates_freebsd + ;; *) if command -v apk > /dev/null; then install_system_updates_apk @@ -765,6 +822,12 @@ install_system_updates_emerge() { emerge --depclean --quiet || throw_error "Failed to clean up obsolete packages" } + +install_system_updates_freebsd() { + sudo pkg update > /dev/null + sudo pkg upgrade --yes > /dev/null +} + install_system_updates_pacman() { sudo pacman -Syu --noconfirm --quiet > /dev/null } @@ -1007,9 +1070,10 @@ readonly BITCOIN_CORE_REPO='https://github.com/bitcoin/bitcoin' readonly NODEBUILDER_REPO='https://github.com/bitcoin-tools/nodebuilder' readonly NODEBUILDER_DEPENDENCIES_TAG='v1.7.1-beta' readonly DEPENDENCIES_BASE_URL="${NODEBUILDER_REPO}/raw/${NODEBUILDER_DEPENDENCIES_TAG}/resources/dependencies" +readonly FREEBSD_DEPENDENCIES_BASE_URL="${NODEBUILDER_REPO}/raw/master/resources/dependencies" case "${TARGET_KERNEL}" in -Linux) +Linux | FreeBSD) SYS_CORES_COUNT="$(nproc)" ;; Darwin) @@ -1018,9 +1082,6 @@ Darwin) MINGW* | Cygwin) throw_error 'Windows is not supported. Use Linux, macOS, or Windows Subsystem for Linux instead.' ;; -FreeBSD) - throw_error "FreeBSD is not supported. Please open an issue at ${NODEBUILDER_REPO}." - ;; *) throw_error "${TARGET_KERNEL} is not supported. Please run on Linux or macOS." ;; @@ -1042,20 +1103,20 @@ fi clear_the_terminal case "${TARGET_KERNEL}" in -Linux) +Linux | FreeBSD) TARGET_BITCOIN_TARBALL_OS='linux-gnu' BITCOIN_DATA_DIRECTORY="${HOME}/.bitcoin" - log_info 'Detected: running Linux.' + log_info "Detected: running ${TARGET_KERNEL}." is_running_in_container && log_info 'Detected: running in Docker.' ;; Darwin) TARGET_BITCOIN_TARBALL_OS='apple-darwin' BITCOIN_DATA_DIRECTORY="${HOME}/Library/Application Support/Bitcoin" - log_info "Detected: running macOS." + log_info 'Detected: running macOS.' [ "${TARGET_ARCHITECTURE}" = 'arm64' ] && display_macos_warning ;; *) - throw_error 'Unknown target kernel type.' + throw_error "Unknown target kernel type '${TARGET_KERNEL}'." ;; esac @@ -1114,7 +1175,8 @@ if [ "${CURRENT_BITCOIN_VERSION_PADDED}" = "${TARGET_BITCOIN_VERSION_PADDED}" ]; log_info "Found Bitcoin Core ${target_bitcoin_version}." elif [ "${compile_bitcoin_flag:-false}" = 'true' ] || [ "${TARGET_OPERATING_SYSTEM}" = 'alpine' ] || - [ "${TARGET_OPERATING_SYSTEM}" = 'gentoo' ]; then + [ "${TARGET_OPERATING_SYSTEM}" = 'gentoo' ] || + [ "${TARGET_OPERATING_SYSTEM}" = 'FreeBSD' ]; then log_info "Failed to find Bitcoin Core ${target_bitcoin_version}." compile_bitcoin_from_source current_bitcoin_version="${target_bitcoin_version}" @@ -1372,9 +1434,9 @@ elif ! grep -q -i '^prune=' "${BITCOIN_CORE_CONFIG_FILE}"; then fi fi -# PRUNING case "${TARGET_KERNEL}" in -Darwin) ;; #TODO: Add macOS memory check +Darwin) ;; +FreeBSD) ;; *) PHYSICAL_MEMORY_TOTAL_IN_MIB="$(get_memory_metric_in_mib 'MemTotal')" && readonly PHYSICAL_MEMORY_TOTAL_IN_MIB PHYSICAL_MEMORY_FREE_IN_MIB="$(get_memory_metric_in_mib 'MemAvailable')" && readonly PHYSICAL_MEMORY_FREE_IN_MIB @@ -1431,10 +1493,11 @@ ibd_status=$(echo "${blockchain_info}" | jq '.initialblockdownload') if [ "${ibd_status}" = 'true' ]; then if ! is_running_in_container; then - log_info 'Disabling system sleep, suspend, and hibernate.' if [ "${TARGET_KERNEL}" = 'Darwin' ]; then + log_info 'Disabling system sleep, suspend, and hibernate.' caffeinate -sw "$(cat "${BITCOIN_DATA_DIRECTORY}/bitcoind.pid")" & - else + elif [ "${TARGET_KERNEL}" = 'Linux' ]; then + log_info 'Disabling system sleep, suspend, and hibernate.' sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target > /dev/null 2>&1 fi log_info "Close this Terminal window by clicking on the 'X'." @@ -1488,7 +1551,7 @@ while [ "${ibd_status}" = 'true' ]; do printf 'Sync progress: %.3f %%\n' "${sync_progress_percent}" printf 'Blocks remaining: %d\n' "$((headers - blocks))" - if [ "${TARGET_KERNEL}" = 'Darwin' ]; then + if [ "${TARGET_KERNEL}" = 'Darwin' ] || [ "${TARGET_KERNEL}" = 'FreeBSD' ]; then current_chain_tip_timestamp="$(/bin/date -r "${last_block_time}" | cut -c 5-)" else current_chain_tip_timestamp="$(date -d @"${last_block_time}" | cut -c 5-)" diff --git a/resources/dependencies/build_dependencies_freebsd.txt b/resources/dependencies/build_dependencies_freebsd.txt new file mode 100644 index 000000000..1093c0da7 --- /dev/null +++ b/resources/dependencies/build_dependencies_freebsd.txt @@ -0,0 +1,16 @@ +autoconf +automake +boost-libs +gettext +gmake +libevent +libqrencode +libtool +libzmq4 +pkgconf +python3 +sqlite3 +qt5-buildtools +qt5-gui +qt5-linguisttools +qt5-widgets diff --git a/resources/dependencies/runtime_dependencies_freebsd.txt b/resources/dependencies/runtime_dependencies_freebsd.txt new file mode 100644 index 000000000..ddd7e7f36 --- /dev/null +++ b/resources/dependencies/runtime_dependencies_freebsd.txt @@ -0,0 +1,6 @@ +curl +git +gnupg +gzip +jq +sudo diff --git a/test/test_nodebuilder b/test/test_nodebuilder index 03ba3c240..b8cbb2715 100755 --- a/test/test_nodebuilder +++ b/test/test_nodebuilder @@ -7,9 +7,11 @@ set -o errexit set -o nounset get_operating_system() { - if [ "${TARGET_KERNEL}" = 'Darwin' ]; then - printf '%s\n' 'Darwin' - else + case "${TARGET_KERNEL}" in + Darwin | FreeBSD) + printf '%s\n' "${TARGET_KERNEL}" + ;; + *) readonly OS_RELEASE_ID_LIKE="$(grep '^ID_LIKE=' /etc/os-release | cut -d= -f2)" readonly OS_RELEASE_ID="$(grep '^ID=' /etc/os-release | cut -d= -f2)" @@ -18,7 +20,8 @@ get_operating_system() { else throw_error 'Failed to determine OS release type' fi - fi + ;; + esac } handle_exit() { @@ -111,7 +114,7 @@ while [ $# -gt 0 ]; do -r | --ref) [ -z "$2" ] && throw_error 'No argument supplied for -r|--ref.' "${LINENO}" readonly GIT_REF_SHORT_NAME="$2" - validate_git_ref_short_name "${GIT_REF_SHORT_NAME}" + [ "$(uname -s)" = 'FreeBSD' ] || validate_git_ref_short_name "${GIT_REF_SHORT_NAME}" shift ;; *) @@ -139,7 +142,7 @@ readonly NODEBUILDER_BINARY_URL="${NODEBUILDER_REPO_URL}/raw/${GIT_REF_SHORT_NAM case "${TARGET_KERNEL}" in Darwin) bitcoin_data_directory="${HOME}/Library/Application Support/Bitcoin" ;; -Linux) bitcoin_data_directory="${HOME}/.bitcoin" ;; +Linux | FreeBSD) bitcoin_data_directory="${HOME}/.bitcoin" ;; MINGW*) throw_error 'Windows is not supported.' ;; *) throw_error 'Your operating system is not supported.' ;; esac @@ -202,7 +205,7 @@ if [ -s test_output_stderr.txt ]; then fi for message in \ - 'INFO: Detected: running (Linux|macOS).$' \ + 'INFO: Detected: running (Linux|macOS|FreeBSD).$' \ 'INFO: Checking for internet.$' \ 'INFO: Internet checks passed.$' \ 'INFO: (Failed to find|Found) Bitcoin Core [1-9][0-9]*\.[0-9]+(\.[0-9]+)?.$' \ @@ -243,6 +246,7 @@ for message in \ done if [ "${COMPILE_BITCOIN}" = 'true' ] || + [ "${TARGET_KERNEL}" = 'FreeBSD' ] || { [ -f /etc/os-release ] && [ "${TARGET_OPERATING_SYSTEM}" = 'alpine' ]; }; then for message in \ 'INFO: Ensuring compile dependencies.$' \ @@ -281,8 +285,7 @@ fi if [ "${TARGET_KERNEL}" != 'Darwin' ]; then for message in \ 'INFO: Ensuring runtime dependencies.$' \ - 'INFO: Creating application shortcuts.$' \ - 'Checking system memory... [0-9]+\.[0-9] of [0-9]+\.[0-9] GiB free.$'; do + 'INFO: Creating application shortcuts.$'; do if ! grep -Eq "${message}" test_output_stdout.txt > /dev/null 2>&1; then printf '%s\n' "FAILURE: Expected message ${message} not found." exit 1 @@ -290,4 +293,12 @@ if [ "${TARGET_KERNEL}" != 'Darwin' ]; then done fi +if [ "${TARGET_KERNEL}" = 'Linux' ]; then + message='Checking system memory... [0-9]+\.[0-9] of [0-9]+\.[0-9] GiB free.$' + if ! grep -Eq "${message}" test_output_stdout.txt > /dev/null 2>&1; then + printf '%s\n' "FAILURE: Expected message ${message} not found." + exit 1 + fi +fi + printf '%s\n' 'PASS: All console output tests passed.'